diff --git a/.circleci/config.yml b/.circleci/config.yml index f2b451306..538d46c07 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ version: 2.1 orbs: aws-ecr: circleci/aws-ecr@8.2.1 - go: circleci/go@1.7.3 + go: circleci/go@1.9.0 jobs: @@ -14,11 +14,12 @@ jobs: resource_class: large steps: - go/install: - version: "1.20" + version: "1.21.4" - checkout - run: name: Print Go environment command: "go env" + - add_ssh_keys - go/load-cache: key: go-mod-v6-{{ checksum "go.sum" }} - go/mod-download @@ -32,31 +33,39 @@ jobs: name: Run tests command: | make test - - run: # sudo is needed, so that integration test binary have proper access to nodes keyring - name: Run integration tests - command: | - sudo -E env "PATH=$PATH" make localnet-test-integration - # TODO: If CI tests will take to long consider having only this e2e test - # instead of separate integration tests and e2e tests. + e2e-test: + machine: + image: ubuntu-2204:2022.10.1 + resource_class: large + steps: + - go/install: + version: "1.21.4" + - checkout + - run: + name: Print Go environment + command: "go env" + - add_ssh_keys - run: name: Run e2e tests command: | - make test-e2e + sudo ln -s /usr/local/go/bin/go /usr/bin/go + sudo make test-e2e lint: machine: image: ubuntu-2204:2022.10.1 resource_class: large steps: - go/install: - version: "1.20" + version: "1.21.4" - checkout + - add_ssh_keys - run: name: Lint proto files command: make proto-lint - run: name: Lint command: | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.2 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.55.2 ./bin/golangci-lint run build_docker: @@ -65,6 +74,7 @@ jobs: resource_class: large steps: - checkout + - add_ssh_keys - aws-ecr/build-image: push-image: false dockerfile: Dockerfile @@ -109,6 +119,9 @@ workflows: build-test: jobs: - build-test + e2e-test: + jobs: + - e2e-test lint: jobs: - lint diff --git a/LICENSE b/LICENSE index a441aa146..45ff37b20 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,99 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2023 Babylonchain, Inc - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Babylonchain, Inc. + +Licensed Work: Babylon + The Licensed Work is (c) 2023 Babylonchain, Inc. + +Additional Use Grant: None. + +Change Date: 2027-01-20 (January 20th, 2027] + +Change License: Apache 2.0 + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/Makefile b/Makefile index 272a682c1..bd525c73e 100644 --- a/Makefile +++ b/Makefile @@ -149,6 +149,9 @@ mocks: $(MOCKS_DIR) $(mockgen_cmd) -source=x/checkpointing/types/expected_keepers.go -package mocks -destination testutil/mocks/checkpointing_expected_keepers.go $(mockgen_cmd) -source=x/checkpointing/keeper/bls_signer.go -package mocks -destination testutil/mocks/bls_signer.go $(mockgen_cmd) -source=x/zoneconcierge/types/expected_keepers.go -package types -destination x/zoneconcierge/types/mocked_keepers.go + $(mockgen_cmd) -source=x/btcstaking/types/expected_keepers.go -package types -destination x/btcstaking/types/mocked_keepers.go + $(mockgen_cmd) -source=x/finality/types/expected_keepers.go -package types -destination x/finality/types/mocked_keepers.go + $(mockgen_cmd) -source=x/incentive/types/expected_keepers.go -package types -destination x/incentive/types/mocked_keepers.go .PHONY: mocks $(MOCKS_DIR): @@ -228,11 +231,7 @@ endif .PHONY: run-tests test test-all $(TEST_TARGETS) -test-integration: - @echo "Running babylon integration test" - @go test github.com/babylonchain/babylon/test -v -count=1 --tags=integration -p 1 - -test-e2e: +test-e2e: build-docker go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e test-sim-nondeterminism: @@ -363,7 +362,7 @@ devdoc-update: ### Protobuf ### ############################################################################### -protoVer=0.12.0 +protoVer=0.14.0 protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer) protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) @@ -392,7 +391,10 @@ proto-lint: build-docker: $(MAKE) -C contrib/images babylond -.PHONY: build-docker +build-cosmos-relayer-docker: + $(MAKE) -C contrib/images cosmos-relayer + +.PHONY: build-docker build-cosmos-relayer-docker ############################################################################### ### Localnet ### @@ -420,9 +422,6 @@ localnet-start: localnet-stop build-docker localnet-start-nodes localnet-stop: docker-compose down -# localnet-test-integration will spin up a localnet and run integration tests on it -localnet-test-integration: localnet-start test-integration localnet-stop - build-test-wasm: docker run --rm -v "$(WASM_DIR)":/code \ --mount type=volume,source="$(WASM_DIR_BASE_NAME)_cache",target=/code/target \ @@ -437,7 +436,6 @@ build-test-wasm: init-testnet-dirs \ localnet-start-nodes \ localnet-start \ -localnet-test-integration \ localnet-stop .PHONY: diagrams diff --git a/README.md b/README.md index e853020a2..3c9ccfbb1 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,64 @@ # Babylon -Bringing Bitcoin security to Cosmos and beyond. +Unlocking 21 Million ₿ to Secure the Decentralized Economy -[![Website](https://badgen.net/badge/icon/website?label=)](https://babylonchain.io) -[![Whitepaper](https://badgen.net/badge/icon/whitepaper?label=)](https://arxiv.org/abs/2207.08392) +[![Website](https://badgen.net/badge/icon/Website?label=)](https://babylonchain.io) [![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/babylon_chain) -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/babylonchain) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.com/invite/babylonglobal) +[![Medium](https://badgen.net/badge/icon/medium?icon=medium&label)](https://medium.com/babylonchain-io) + +[Babylon](https://babylonchain.io) provides a suite of security-sharing +protocols between Bitcoin and the PoS world. It provides two inter-connected +protocols: + +- **Bitcoin timestamping:** Submits succinct and verifiable timestamps of any + data (such as PoS blockchains) to Bitcoin. +- **Bitcoin staking:** Enables Bitcoin holders to provide economic security to + any decentralized system through trustless (and self-custodian) staking. + +[![BTC staking +litepaper](https://badgen.net/badge/icon/BTC%20staking%20litepaper?label=)](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf) +[![BTC timestamping +whitepaper](https://badgen.net/badge/icon/BTC%20timestamping%20whitepaper?label=)](https://arxiv.org/abs/2207.08392) ## Build and install -The babylond application based on the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) is the main application of the Babylon network. -This repository is used to build the Babylon core application to join the Babylon network. +This repository contains the Golang implementation of the Babylon node. It is +based on the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk). ### Requirements -To build and install, you need to have Go 1.19 available. -Follow the instructions on the [Golang page](https://go.dev/doc/install) to do that. + +To build and install, you need to have Go 1.21 available. Follow the +instructions on the [Golang page](https://go.dev/doc/install) to do that. To build the binary: + ```console make build ``` The binary will then be available at `./build/babylond` . -To install: +To install the binary to system directories: + ```console make install ``` ## Documentation -For the most up-to-date documentation please visit [docs.babylonchain.io](https://docs.babylonchain.io) +For user-facing documents, please visit +[docs.babylonchain.io](https://docs.babylonchain.io). For technical documents +about high-level designs of Babylon, please visit +[docs/README.md](./docs/README.md). Each module under `x/` also contains a +document about its design and implementation. ## Joining the testnet -Please follow the instructions on the [Joining the Testnet documentation page](https://docs.babylonchain.io/docs/testnet/overview). +Please follow the instructions on the [User +Guides](https://docs.babylonchain.io/docs/user-guides/). ## Contributing -The [docs](./docs) directory contains the necessary information on how to get started using the babylond executable for development purposes. +The [docs](./docs) directory contains the necessary information on how to get +started using the babylond executable for development purposes. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e30d358d5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,82 @@ +# Security Policy + +## Introduction + +Security researchers are essential in identifying vulnerabilities that may impact the Babylon ecosystem. +If you have discovered a security vulnerability in the Babylon chain or any repository managed by Babylon, +we encourage you to notify us using one of the methods outlined below. + +### Guidelines for Responsible Vulnerability Testing and Reporting + +1. **Refrain from testing vulnerabilities on our publicly accessible environments**, including but not limited to: + - Babylon mainnet. + - Babylon frontend. + - Babylon public testnets. + - Babylon testnet frontend. +2. **Avoid reporting security vulnerabilities through public channels, including GitHub issues.** + +## Reporting Security Issues + +To privately report a security vulnerability, please choose one of the following options: + +### 1. Email + +Send your detailed vulnerability report to `security@babylonchain.io`. + +### 2. GitHub Private Vulnerability Reporting + +Utilize [GitHub's Private Vulnerability Reporting](https://github.com/babylonchain/babylon/security/advisories/new) +for confidential disclosure. + +## Submit Vulnerability Report + +When reporting a vulnerability through either method, please include the following details to aid in our assessment: + +- Type of vulnerability. +- Description of the vulnerability. +- Steps to reproduce the issue. +- Impact of the issue. +- Explanation of how an attacker could exploit it. + +## Vulnerability Disclosure Process + +1. **Initial Report**: Submit the vulnerability via one of the above channels. +2. **Confirmation**: We will confirm receipt of your report within 48 hours. +3. **Assessment**: Our security team will evaluate the vulnerability and inform you of its severity and the estimated + time frame for resolution. +4. **Resolution**: Once fixed, you will be contacted to verify the solution. +5. **Public Disclosure**: Details of the vulnerability may be publicly disclosed after ensuring it poses no further risk. + +During the vulnerability disclosure process, we ask security researchers to keep vulnerabilities and communications +around vulnerability submissions private and confidential until a patch is developed. Should a security issue require +a network upgrade, additional time may be needed to raise a governance proposal and complete the upgrade. + +During this time: + +- Avoid exploiting any vulnerabilities you discover. +- Demonstrate good faith by not disrupting or degrading Babylon's services. + +## Severity Characterization + +| Severity | Description | +|--------------|--------------------------------------------------------------------------| +| **CRITICAL** | Immediate threat to critical systems (e.g., chain halts, funds at risk). | +| **HIGH** | Significant impact on major functionality. | +| **MEDIUM** | Impacts minor features or exposes non-sensitive data. | +| **LOW** | Minimal impact. | + +## Bug Bounty + +Though we don't have an official bug bounty program, we generally offer rewards to security researchers who responsibly +disclose vulnerabilities to us. Bounties are generally awarded for vulnerabilities classified as **high** or +**critical** severity. Bounty amounts will be determined during the disclosure process, after the severity has been +assessed. +We are setting a bug bounty program at the moment. Rewards will be offered in retrospective, once the program is in +place. + +> [!WARNING] +> Targeting our production environments will disqualify you from receiving any bounty. + +## Feedback on this Policy + +For recommendations on how to improve this policy, either submit a pull request or email `security@babylonchain.io`. diff --git a/app/ante_btc_validation_decorator.go b/app/ante_btc_validation_decorator.go index 109789023..79f800257 100644 --- a/app/ante_btc_validation_decorator.go +++ b/app/ante_btc_validation_decorator.go @@ -38,10 +38,9 @@ func (bvd BtcValidationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat return ctx, btccheckpointtypes.ErrInvalidCheckpointProof.Wrap(err.Error()) } - case *btclightclient.MsgInsertHeader: + case *btclightclient.MsgInsertHeaders: powLimit := bvd.BtcCfg.PowLimit() - err := msg.ValidateHeader(&powLimit) - + err := msg.ValidateHeaders(&powLimit) if err != nil { return ctx, btclightclient.ErrInvalidProofOfWOrk } diff --git a/app/app.go b/app/app.go index c878a001f..9d9aa6436 100644 --- a/app/app.go +++ b/app/app.go @@ -6,49 +6,56 @@ import ( "io" "os" "path/filepath" - "strings" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - + "cosmossdk.io/client/v2/autocli" + "cosmossdk.io/core/appmodule" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/circuit" + circuitkeeper "cosmossdk.io/x/circuit/keeper" + circuittypes "cosmossdk.io/x/circuit/types" + "cosmossdk.io/x/evidence" + evidencekeeper "cosmossdk.io/x/evidence/keeper" + evidencetypes "cosmossdk.io/x/evidence/types" + "cosmossdk.io/x/feegrant" + feegrantkeeper "cosmossdk.io/x/feegrant/keeper" + feegrantmodule "cosmossdk.io/x/feegrant/module" + "cosmossdk.io/x/upgrade" + upgradekeeper "cosmossdk.io/x/upgrade/keeper" + upgradetypes "cosmossdk.io/x/upgrade/types" wasmapp "github.com/CosmWasm/wasmd/app" "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - "github.com/babylonchain/babylon/client/docs" - bbn "github.com/babylonchain/babylon/types" - owasm "github.com/babylonchain/babylon/wasmbinding" - nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" - runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" - "github.com/cosmos/cosmos-sdk/x/consensus" - consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" - consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - - dbm "github.com/cometbft/cometbft-db" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/log" - tmos "github.com/cometbft/cometbft/libs/os" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" - "github.com/spf13/cast" - - errorsmod "cosmossdk.io/errors" + cmtos "github.com/cometbft/cometbft/libs/os" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/std" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -61,26 +68,22 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/capability" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/evidence" - evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" - "github.com/cosmos/cosmos-sdk/x/feegrant" - feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" - feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" "github.com/cosmos/cosmos-sdk/x/mint" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" @@ -95,57 +98,62 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/x/upgrade" - upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" - upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/cosmos/gogoproto/proto" + "github.com/cosmos/ibc-go/modules/capability" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v8/modules/core" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" // ibc module puts types under `ibchost` rather than `ibctypes` + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + "github.com/spf13/cast" appparams "github.com/babylonchain/babylon/app/params" - + "github.com/babylonchain/babylon/client/docs" + bbn "github.com/babylonchain/babylon/types" + owasm "github.com/babylonchain/babylon/wasmbinding" "github.com/babylonchain/babylon/x/btccheckpoint" btccheckpointkeeper "github.com/babylonchain/babylon/x/btccheckpoint/keeper" btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" "github.com/babylonchain/babylon/x/btclightclient" btclightclientkeeper "github.com/babylonchain/babylon/x/btclightclient/keeper" btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking" + btcstakingkeeper "github.com/babylonchain/babylon/x/btcstaking/keeper" + btcstakingtypes "github.com/babylonchain/babylon/x/btcstaking/types" "github.com/babylonchain/babylon/x/checkpointing" checkpointingkeeper "github.com/babylonchain/babylon/x/checkpointing/keeper" checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" "github.com/babylonchain/babylon/x/epoching" epochingkeeper "github.com/babylonchain/babylon/x/epoching/keeper" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + "github.com/babylonchain/babylon/x/finality" + finalitykeeper "github.com/babylonchain/babylon/x/finality/keeper" + finalitytypes "github.com/babylonchain/babylon/x/finality/types" + "github.com/babylonchain/babylon/x/incentive" + incentivekeeper "github.com/babylonchain/babylon/x/incentive/keeper" + incentivetypes "github.com/babylonchain/babylon/x/incentive/types" "github.com/babylonchain/babylon/x/monitor" monitorkeeper "github.com/babylonchain/babylon/x/monitor/keeper" monitortypes "github.com/babylonchain/babylon/x/monitor/types" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - govclient "github.com/cosmos/cosmos-sdk/x/gov/client" - 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" - ibc "github.com/cosmos/ibc-go/v7/modules/core" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" // ibc module puts types under `ibchost` rather than `ibctypes` - ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - - // IBC-related "github.com/babylonchain/babylon/x/zoneconcierge" zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) const ( appName = "BabylonApp" - // Custom prefix for application enviromental variables. + // Custom prefix for application environmental variables. // From cosmos version 0.46 is is possible to have custom prefix for application - // enviromental variables - https://github.com/cosmos/cosmos-sdk/pull/10950 + // environmental variables - https://github.com/cosmos/cosmos-sdk/pull/10950 BabylonAppEnvPrefix = "" // TODO review possible capabilities @@ -159,60 +167,16 @@ const ( // https://medium.com/cosmwasm/cosmwasm-for-ctos-iv-native-integrations-713140bf75fc // suggests 50M as reasonable limits. Me may want to adjust it later. DefaultGasLimit int64 = 50000000 + + DefaultVoteExtensionsEnableHeight = 1 ) var ( // DefaultNodeHome default home directories for the application daemon DefaultNodeHome string - // ModuleBasics defines the module BasicManager is in charge of setting up basic, - // non-dependant module elements, such as codec registration - // and genesis verification. - ModuleBasics = module.NewBasicManager( - auth.AppModuleBasic{}, - genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), - bank.AppModuleBasic{}, - capability.AppModuleBasic{}, - staking.AppModuleBasic{}, - mint.AppModuleBasic{}, - distr.AppModuleBasic{}, - gov.NewAppModuleBasic( - []govclient.ProposalHandler{ - paramsclient.ProposalHandler, - upgradeclient.LegacyProposalHandler, - upgradeclient.LegacyCancelProposalHandler, - ibcclientclient.UpdateClientProposalHandler, - ibcclientclient.UpgradeProposalHandler, - }, - ), - params.AppModuleBasic{}, - consensus.AppModuleBasic{}, - crisis.AppModuleBasic{}, - slashing.AppModuleBasic{}, - feegrantmodule.AppModuleBasic{}, - upgrade.AppModuleBasic{}, - evidence.AppModuleBasic{}, - authzmodule.AppModuleBasic{}, - vesting.AppModuleBasic{}, - wasm.AppModuleBasic{}, - - // Babylon modules - epoching.AppModuleBasic{}, - btclightclient.AppModuleBasic{}, - btccheckpoint.AppModuleBasic{}, - checkpointing.AppModuleBasic{}, - monitor.AppModuleBasic{}, - - // IBC-related - ibc.AppModuleBasic{}, - ibctm.AppModuleBasic{}, - transfer.AppModuleBasic{}, - zoneconcierge.AppModuleBasic{}, - ibcfee.AppModuleBasic{}, - ) - - // module account permissions + // fee collector account, module accounts and their permissions maccPerms = map[string][]string{ - authtypes.FeeCollectorName: nil, + authtypes.FeeCollectorName: nil, // fee collector account distrtypes.ModuleName: nil, minttypes.ModuleName: {authtypes.Minter}, stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, @@ -220,53 +184,18 @@ var ( 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}, + incentivetypes.ModuleName: nil, // this line is needed to create an account for incentive module } ) // Wasm related variables var ( - // WasmProposalsEnabled enables all x/wasm proposals when its value is "true" - // and EnableSpecificWasmProposals is empty. Otherwise, all x/wasm proposals - // are disabled. - WasmProposalsEnabled = "true" - - // EnableSpecificWasmProposals, if set, must be comma-separated list of values - // that are all a subset of "EnableAllProposals", which takes precedence over - // WasmProposalsEnabled. - // - // See: https://github.com/CosmWasm/wasmd/blob/02a54d33ff2c064f3539ae12d75d027d9c665f05/x/wasm/internal/types/proposal.go#L28-L34 - EnableSpecificWasmProposals = "" - // EmptyWasmOpts defines a type alias for a list of wasm options. - EmptyWasmOpts []wasm.Option + EmptyWasmOpts []wasmkeeper.Option ) -// GetWasmEnabledProposals parses the WasmProposalsEnabled and -// EnableSpecificWasmProposals values to produce a list of enabled proposals to -// pass into the application. -func GetWasmEnabledProposals() []wasm.ProposalType { - if EnableSpecificWasmProposals == "" { - if WasmProposalsEnabled == "true" { - return wasm.EnableAllProposals - } - - return wasm.DisableAllProposals - } - - chunks := strings.Split(EnableSpecificWasmProposals, ",") - - proposals, err := wasm.ConvertToProposals(chunks) - if err != nil { - panic(err) - } - - return proposals -} - var ( - _ App = (*BabylonApp)(nil) + _ runtime.AppI = (*BabylonApp)(nil) _ servertypes.Application = (*BabylonApp)(nil) ) @@ -304,6 +233,7 @@ type BabylonApp struct { EvidenceKeeper evidencekeeper.Keeper FeeGrantKeeper feegrantkeeper.Keeper ConsensusParamsKeeper consensusparamkeeper.Keeper + CircuitKeeper circuitkeeper.Keeper // Babylon modules EpochingKeeper epochingkeeper.Keeper @@ -318,7 +248,16 @@ type BabylonApp struct { TransferKeeper ibctransferkeeper.Keeper // for cross-chain fungible token transfers ZoneConciergeKeeper zckeeper.Keeper // for cross-chain fungible token transfers - WasmKeeper wasm.Keeper + // BTC staking related modules + BTCStakingKeeper btcstakingkeeper.Keeper + FinalityKeeper finalitykeeper.Keeper + + // wasm smart contract module + WasmKeeper wasmkeeper.Keeper + + // tokenomics-related modules + IncentiveKeeper incentivekeeper.Keeper + // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper ScopedTransferKeeper capabilitykeeper.ScopedKeeper @@ -326,7 +265,8 @@ type BabylonApp struct { ScopedWasmKeeper capabilitykeeper.ScopedKeeper // the module manager - mm *module.Manager + ModuleManager *module.Manager + BasicModuleManager module.BasicManager // simulation manager sm *module.SimulationManager @@ -348,32 +288,34 @@ func init() { // NewBabylonApp returns a reference to an initialized BabylonApp. func NewBabylonApp( logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool, - homePath string, invCheckPeriod uint, encodingConfig appparams.EncodingConfig, privSigner *PrivSigner, + invCheckPeriod uint, privSigner *PrivSigner, appOpts servertypes.AppOptions, - wasmEnabledProposals []wasm.ProposalType, - wasmOpts []wasm.Option, + wasmOpts []wasmkeeper.Option, baseAppOptions ...func(*baseapp.BaseApp), ) *BabylonApp { // we could also take it from global object which should be initilised in rootCmd // but this way it makes babylon app more testable btcConfig := bbn.ParseBtcOptionsFromConfig(appOpts) powLimit := btcConfig.PowLimit() + btcNetParams := btcConfig.NetParams() + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + if homePath == "" { + homePath = DefaultNodeHome + } - appCodec := encodingConfig.Marshaler - legacyAmino := encodingConfig.Amino - interfaceRegistry := encodingConfig.InterfaceRegistry - txConfig := encodingConfig.TxConfig - bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...) - bApp.SetCommitMultiStoreTracer(traceStore) - bApp.SetVersion(version.Version) - bApp.SetInterfaceRegistry(interfaceRegistry) - bApp.SetTxEncoder(txConfig.TxEncoder()) + encCfg := appparams.DefaultEncodingConfig() + interfaceRegistry := encCfg.InterfaceRegistry + appCodec := encCfg.Codec + legacyAmino := encCfg.Amino + txConfig := encCfg.TxConfig + std.RegisterLegacyAminoCodec(legacyAmino) + std.RegisterInterfaces(interfaceRegistry) - keys := sdk.NewKVStoreKeys( + keys := storetypes.NewKVStoreKeys( authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, - evidencetypes.StoreKey, capabilitytypes.StoreKey, + evidencetypes.StoreKey, circuittypes.StoreKey, capabilitytypes.StoreKey, authzkeeper.StoreKey, // Babylon modules epochingtypes.StoreKey, @@ -386,14 +328,82 @@ func NewBabylonApp( ibctransfertypes.StoreKey, ibcfeetypes.StoreKey, zctypes.StoreKey, - wasm.StoreKey, + // BTC staking related modules + btcstakingtypes.StoreKey, + finalitytypes.StoreKey, + // WASM + wasmtypes.StoreKey, + // tokenomics-related modules + incentivetypes.StoreKey, + ) + accountKeeper := authkeeper.NewAccountKeeper( + appCodec, + runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, + maccPerms, + authcodec.NewBech32Codec(appparams.Bech32PrefixAccAddr), + appparams.Bech32PrefixAccAddr, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - tkeys := sdk.NewTransientStoreKeys( + bankKeeper := bankkeeper.NewBaseKeeper( + appCodec, + runtime.NewKVStoreService(keys[banktypes.StoreKey]), + accountKeeper, + BlockedAddresses(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + logger, + ) + + stakingKeeper := stakingkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), + accountKeeper, + bankKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + authcodec.NewBech32Codec(appparams.Bech32PrefixValAddr), + authcodec.NewBech32Codec(appparams.Bech32PrefixConsAddr), + ) + + // NOTE: the epoching module has to be set before the chekpointing module, as the checkpointing module will have access to the epoching module + epochingKeeper := epochingkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[epochingtypes.StoreKey]), + bankKeeper, + stakingKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + checkpointingKeeper := checkpointingkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[checkpointingtypes.StoreKey]), + privSigner.WrappedPV, + epochingKeeper, + ) + + bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + bApp.SetTxEncoder(txConfig.TxEncoder()) + + // set handlers of vote extension + voteExtHandler := checkpointing.NewVoteExtensionHandler(logger, &checkpointingKeeper) + voteExtHandler.SetHandlers(bApp) + proposalHandler := checkpointing.NewProposalHandler( + logger, &checkpointingKeeper, bApp.Mempool(), bApp) + proposalHandler.SetHandlers(bApp) + + tkeys := storetypes.NewTransientStoreKeys( paramstypes.TStoreKey, btccheckpointtypes.TStoreKey) // NOTE: The testingkey is just mounted for testing purposes. Actual applications should // not include this key. - memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") + memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") + + // register streaming services + if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil { + panic(err) + } app := &BabylonApp{ BaseApp: bApp, @@ -407,67 +417,128 @@ func NewBabylonApp( memKeys: memKeys, } - app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + app.ParamsKeeper = initParamsKeeper( + appCodec, + legacyAmino, + keys[paramstypes.StoreKey], + tkeys[paramstypes.TStoreKey], + ) - app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String()) - bApp.SetParamStore(&app.ConsensusParamsKeeper) + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + runtime.EventService{}, + ) + bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore) - app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + app.CapabilityKeeper = capabilitykeeper.NewKeeper( + appCodec, + keys[capabilitytypes.StoreKey], + memKeys[capabilitytypes.MemStoreKey], + ) // grant capabilities for the ibc and ibc-transfer modules scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) scopedZoneConciergeKeeper := app.CapabilityKeeper.ScopeToModule(zctypes.ModuleName) - scopedWasmKeeper := app.CapabilityKeeper.ScopeToModule(wasm.ModuleName) + scopedWasmKeeper := app.CapabilityKeeper.ScopeToModule(wasmtypes.ModuleName) // Applications that wish to enforce statically created ScopedKeepers should call `Seal` after creating // their scoped modules in `NewApp` with `ScopeToModule` app.CapabilityKeeper.Seal() - // TODO: Grant capabilities for the ibc and ibc-transfer modules - // add keepers - app.AccountKeeper = authkeeper.NewAccountKeeper(appCodec, keys[authtypes.StoreKey], authtypes.ProtoBaseAccount, maccPerms, appparams.Bech32PrefixAccAddr, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.AccountKeeper = accountKeeper + + app.BankKeeper = bankKeeper - app.BankKeeper = bankkeeper.NewBaseKeeper( + app.StakingKeeper = stakingKeeper + + app.CircuitKeeper = circuitkeeper.NewKeeper( appCodec, - keys[banktypes.StoreKey], - app.AccountKeeper, - BlockedAddresses(), + runtime.NewKVStoreService(keys[circuittypes.StoreKey]), authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.AccountKeeper.AddressCodec(), ) + app.BaseApp.SetCircuitBreaker(&app.CircuitKeeper) - app.StakingKeeper = stakingkeeper.NewKeeper( - appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[minttypes.StoreKey]), + app.StakingKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - // NOTE: the epoching module has to be set before the chekpointing module, as the checkpointing module will have access to the epoching module - epochingKeeper := epochingkeeper.NewKeeper( - appCodec, keys[epochingtypes.StoreKey], keys[epochingtypes.StoreKey], app.BankKeeper, app.StakingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[distrtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - app.MintKeeper = mintkeeper.NewKeeper(appCodec, keys[minttypes.StoreKey], app.StakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) - - app.DistrKeeper = distrkeeper.NewKeeper(appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + // set up incentive keeper + app.IncentiveKeeper = incentivekeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[incentivetypes.StoreKey]), + app.BankKeeper, + app.AccountKeeper, + &epochingKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + authtypes.FeeCollectorName, + ) app.SlashingKeeper = slashingkeeper.NewKeeper( - appCodec, legacyAmino, keys[slashingtypes.StoreKey], app.StakingKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + appCodec, + legacyAmino, + runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), + app.StakingKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - app.CrisisKeeper = crisiskeeper.NewKeeper(appCodec, keys[crisistypes.StoreKey], invCheckPeriod, - app.BankKeeper, authtypes.FeeCollectorName, authtypes.NewModuleAddress(govtypes.ModuleName).String()) - - app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) - // set the governance module account as the authority for conducting upgrades - app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.CrisisKeeper = crisiskeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[crisistypes.StoreKey]), + invCheckPeriod, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.AccountKeeper.AddressCodec(), + ) + app.FeeGrantKeeper = feegrantkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[feegrant.StoreKey]), + app.AccountKeeper, + ) // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks app.StakingKeeper.SetHooks( stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks(), epochingKeeper.Hooks()), ) - app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper) + // set the governance module account as the authority for conducting upgrades + app.UpgradeKeeper = upgradekeeper.NewKeeper( + skipUpgradeHeights, + runtime.NewKVStoreService(keys[upgradetypes.StoreKey]), + appCodec, + homePath, + app.BaseApp, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.AuthzKeeper = authzkeeper.NewKeeper( + runtime.NewKVStoreService(keys[authzkeeper.StoreKey]), + appCodec, + app.MsgServiceRouter(), + app.AccountKeeper, + ) app.IBCKeeper = ibckeeper.NewKeeper( appCodec, @@ -476,11 +547,13 @@ func NewBabylonApp( app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, + // From 8.0.0 the IBC keeper requires an authority for the messages + // `MsgIBCSoftwareUpgrade` and `MsgRecoverClient` + // https://github.com/cosmos/ibc-go/releases/tag/v8.0.0 + // Gov is the proper authority for those types of messages + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - // ... other modules keepers - // TODO: Create IBC keeper - // register the proposal types // Deprecated: Avoid adding new handlers, instead use the new proposal flow // by granting the governance module the right to execute the message. @@ -488,20 +561,24 @@ func NewBabylonApp( // TODO: investigate how to migrate to new proposal flow govRouter := govv1beta1.NewRouter() govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). - AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). - AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). - AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)) + // TODO: this should be a function parameter govConfig := govtypes.DefaultConfig() - /* Example of setting gov params: govConfig.MaxMetadataLen = 10000 */ govKeeper := govkeeper.NewKeeper( - appCodec, keys[govtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - app.StakingKeeper, app.MsgServiceRouter(), govConfig, authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) + appCodec, + runtime.NewKVStoreService(keys[govtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.DistrKeeper, + app.MsgServiceRouter(), + govConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String()) app.GovKeeper = *govKeeper.SetHooks( govtypes.NewMultiGovHooks( @@ -511,36 +588,24 @@ func NewBabylonApp( btclightclientKeeper := btclightclientkeeper.NewKeeper( appCodec, - keys[btclightclienttypes.StoreKey], - keys[btclightclienttypes.MemStoreKey], + runtime.NewKVStoreService(keys[btclightclienttypes.StoreKey]), btcConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - checkpointingKeeper := checkpointingkeeper.NewKeeper( - appCodec, - keys[checkpointingtypes.StoreKey], - keys[checkpointingtypes.MemStoreKey], - privSigner.WrappedPV, - &epochingKeeper, - privSigner.ClientCtx, - ) + btcCheckpointKeeper := btccheckpointkeeper.NewKeeper( appCodec, - keys[btccheckpointtypes.StoreKey], + runtime.NewKVStoreService(keys[btccheckpointtypes.StoreKey]), tkeys[btccheckpointtypes.TStoreKey], - keys[btccheckpointtypes.MemStoreKey], &btclightclientKeeper, &checkpointingKeeper, + &app.IncentiveKeeper, &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 { - panic(fmt.Errorf("couldn't get client from nodeURI %s: %w", privSigner.ClientCtx.NodeURI, err)) - } // create querier for KVStore - storeQuerier, ok := app.CommitMultiStore().(sdk.Queryable) + storeQuerier, ok := app.CommitMultiStore().(storetypes.Queryable) if !ok { panic(errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries")) } @@ -549,58 +614,56 @@ func NewBabylonApp( appCodec, keys[ibcfeetypes.StoreKey], app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware app.IBCKeeper.ChannelKeeper, - &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, ) zcKeeper := zckeeper.NewKeeper( appCodec, - keys[zctypes.StoreKey], - keys[zctypes.MemStoreKey], + runtime.NewKVStoreService(keys[zctypes.StoreKey]), app.IBCFeeKeeper, + app.IBCKeeper.ClientKeeper, app.IBCKeeper.ChannelKeeper, - &app.IBCKeeper.PortKeeper, + app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, &btclightclientKeeper, &checkpointingKeeper, &btcCheckpointKeeper, epochingKeeper, - tmClient, storeQuerier, scopedZoneConciergeKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - - // replace IBC keeper's client keeper with our ExtendedKeeper - extendedClientKeeper := extendedkeeper.NewExtendedKeeper(appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), app.StakingKeeper, app.UpgradeKeeper) - // make zcKeeper to hooks onto extendedClientKeeper so that zcKeeper can receive notifications of new headers - extendedClientKeeper = *extendedClientKeeper.SetHooks( - extendedkeeper.NewMultiClientHooks(zcKeeper.Hooks()), - ) - app.IBCKeeper.ClientKeeper = extendedClientKeeper - app.ZoneConciergeKeeper = *zcKeeper // Create Transfer Keepers app.TransferKeeper = ibctransferkeeper.NewKeeper( - appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), - app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, + appCodec, + keys[ibctransfertypes.StoreKey], + app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCFeeKeeper, + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, + app.AccountKeeper, + app.BankKeeper, + scopedTransferKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) app.MonitorKeeper = monitorkeeper.NewKeeper( appCodec, - keys[monitortypes.StoreKey], - keys[monitortypes.StoreKey], + runtime.NewKVStoreService(keys[monitortypes.StoreKey]), &btclightclientKeeper, ) // 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 + // make ZoneConcierge and Monitor to subscribe to the epoching's hooks app.EpochingKeeper = *epochingKeeper.SetHooks( epochingtypes.NewMultiEpochingHooks(app.ZoneConciergeKeeper.Hooks(), app.MonitorKeeper.Hooks()), ) + + // set up Checkpointing, BTCCheckpoint, and BTCLightclient keepers app.CheckpointingKeeper = *checkpointingKeeper.SetHooks( checkpointingtypes.NewMultiCheckpointingHooks(app.EpochingKeeper.Hooks(), app.ZoneConciergeKeeper.Hooks(), app.MonitorKeeper.Hooks()), ) @@ -609,14 +672,36 @@ func NewBabylonApp( btclightclienttypes.NewMultiBTCLightClientHooks(app.BtcCheckpointKeeper.Hooks()), ) + // set up BTC staking keeper + app.BTCStakingKeeper = btcstakingkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[btcstakingtypes.StoreKey]), + &btclightclientKeeper, + &btcCheckpointKeeper, + btcNetParams, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // set up finality keeper + app.FinalityKeeper = finalitykeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[finalitytypes.StoreKey]), + app.BTCStakingKeeper, + app.IncentiveKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // create evidence keeper with router evidenceKeeper := evidencekeeper.NewKeeper( - appCodec, keys[evidencetypes.StoreKey], app.StakingKeeper, app.SlashingKeeper, + appCodec, + runtime.NewKVStoreService(keys[evidencetypes.StoreKey]), + app.StakingKeeper, + app.SlashingKeeper, + app.AccountKeeper.AddressCodec(), + runtime.ProvideCometInfoService(), ) // If evidence needs to be handled for the app, set routes in router here and seal app.EvidenceKeeper = *evidenceKeeper - wasmDir := filepath.Join(homePath, "wasm") wasmConfig, err := wasm.ReadWasmConfig(appOpts) if err != nil { panic(fmt.Sprintf("error while reading wasm config: %s", err)) @@ -624,27 +709,30 @@ func NewBabylonApp( wasmOpts = append(owasm.RegisterCustomPlugins(&app.EpochingKeeper, &app.ZoneConciergeKeeper, &app.BTCLightClientKeeper), wasmOpts...) - app.WasmKeeper = wasm.NewKeeper( + app.WasmKeeper = wasmkeeper.NewKeeper( appCodec, - keys[wasm.StoreKey], + runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, distrkeeper.NewQuerier(app.DistrKeeper), app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, - &app.IBCKeeper.PortKeeper, + app.IBCKeeper.PortKeeper, scopedWasmKeeper, app.TransferKeeper, app.MsgServiceRouter(), app.GRPCQueryRouter(), - wasmDir, + homePath, wasmConfig, wasmCapabilities, authtypes.NewModuleAddress(govtypes.ModuleName).String(), wasmOpts..., ) + // Set legacy router for backwards compatibility with gov v1beta1 + app.GovKeeper.SetLegacyRouter(govRouter) + // Create all supported IBC routes var transferStack porttypes.IBCModule transferStack = transfer.NewIBCModule(app.TransferKeeper) @@ -662,20 +750,12 @@ func NewBabylonApp( ibcRouter := porttypes.NewRouter(). AddRoute(ibctransfertypes.ModuleName, transferStack). AddRoute(zctypes.ModuleName, zoneConciergeStack). - AddRoute(wasm.ModuleName, wasmStack) + AddRoute(wasmtypes.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)) - } - - // Set legacy router for backwards compatibility with gov v1beta1 - app.GovKeeper.SetLegacyRouter(govRouter) - /**** Module Options ****/ // NOTE: we may consider parsing `appOpts` inside module constructors. For the moment @@ -684,39 +764,70 @@ func NewBabylonApp( // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. - app.mm = module.NewManager( + app.ModuleManager = module.NewManager( genutil.NewAppModule( - app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, - encodingConfig.TxConfig, + app.AccountKeeper, + app.StakingKeeper, + app, + txConfig, ), auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), - capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), - slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), app.interfaceRegistry), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), - upgrade.NewAppModule(app.UpgradeKeeper), + upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), evidence.NewAppModule(app.EvidenceKeeper), params.NewAppModule(app.ParamsKeeper), consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), + circuit.NewAppModule(appCodec, app.CircuitKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasm.ModuleName)), - // Babylon modules - epoching.NewAppModule(appCodec, app.EpochingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - btclightclient.NewAppModule(appCodec, app.BTCLightClientKeeper, app.AccountKeeper, app.BankKeeper), - btccheckpoint.NewAppModule(appCodec, app.BtcCheckpointKeeper, app.AccountKeeper, app.BankKeeper), - checkpointing.NewAppModule(appCodec, app.CheckpointingKeeper, app.AccountKeeper, app.BankKeeper), - monitor.NewAppModule(appCodec, app.MonitorKeeper, app.AccountKeeper, app.BankKeeper), - // IBC-related modules + // non sdk modules + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), + wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasmtypes.ModuleName)), ibc.NewAppModule(app.IBCKeeper), transfer.NewAppModule(app.TransferKeeper), - zoneconcierge.NewAppModule(appCodec, app.ZoneConciergeKeeper, app.AccountKeeper, app.BankKeeper), ibcfee.NewAppModule(app.IBCFeeKeeper), + ibctm.AppModule{}, + // Babylon modules - btc timestamping + epoching.NewAppModule(appCodec, app.EpochingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + btclightclient.NewAppModule(appCodec, app.BTCLightClientKeeper), + btccheckpoint.NewAppModule(appCodec, app.BtcCheckpointKeeper), + checkpointing.NewAppModule(appCodec, app.CheckpointingKeeper), + monitor.NewAppModule(appCodec, app.MonitorKeeper), + zoneconcierge.NewAppModule(appCodec, app.ZoneConciergeKeeper, app.AccountKeeper, app.BankKeeper), + // Babylon modules - btc staking + btcstaking.NewAppModule(appCodec, app.BTCStakingKeeper), + finality.NewAppModule(appCodec, app.FinalityKeeper), + // Babylon modules - tokenomics + incentive.NewAppModule(appCodec, app.IncentiveKeeper, app.AccountKeeper, app.BankKeeper), + ) + + // BasicModuleManager defines the module BasicManager which is in charge of setting up basic, + // non-dependant module elements, such as codec registration and genesis verification. + // By default, it is composed of all the modules from the module manager. + // Additionally, app module basics can be overwritten by passing them as an argument. + app.BasicModuleManager = module.NewBasicManagerFromManager( + app.ModuleManager, + map[string]module.AppModuleBasic{ + genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + govtypes.ModuleName: gov.NewAppModuleBasic( + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + }, + ), + }) + app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino) + app.BasicModuleManager.RegisterInterfaces(interfaceRegistry) + + // NOTE: upgrade module is required to be prioritized + app.ModuleManager.SetOrderPreBlockers( + upgradetypes.ModuleName, ) // During begin block slashing happens after distr.BeginBlocker so that @@ -724,12 +835,16 @@ func NewBabylonApp( // CanWithdrawInvariant invariant. // NOTE: staking module is required if HistoricalEntries param > 0 // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) - app.mm.SetOrderBeginBlockers( - upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + app.ModuleManager.SetOrderBeginBlockers( + upgradetypes.ModuleName, capabilitytypes.ModuleName, + // NOTE: incentive module's BeginBlock has to be after mint but before distribution + // so that it can intercept a part of new inflation to reward BTC staking/timestamping stakeholders + minttypes.ModuleName, incentivetypes.ModuleName, distrtypes.ModuleName, + slashingtypes.ModuleName, evidencetypes.ModuleName, stakingtypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, authz.ModuleName, feegrant.ModuleName, - paramstypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, + paramstypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, circuittypes.ModuleName, // Babylon modules epochingtypes.ModuleName, btclightclienttypes.ModuleName, @@ -741,7 +856,10 @@ func NewBabylonApp( ibctransfertypes.ModuleName, zctypes.ModuleName, ibcfeetypes.ModuleName, - wasm.ModuleName, + wasmtypes.ModuleName, + // BTC staking related modules + btcstakingtypes.ModuleName, + finalitytypes.ModuleName, ) // TODO: there will be an architecture design on whether to modify slashing/evidence, specifically // - how many validators can we slash in a single epoch and @@ -749,7 +867,7 @@ func NewBabylonApp( // app.mm.OrderBeginBlockers = append(app.mm.OrderBeginBlockers[:4], app.mm.OrderBeginBlockers[4+1:]...) // remove slashingtypes.ModuleName // app.mm.OrderBeginBlockers = append(app.mm.OrderBeginBlockers[:4], app.mm.OrderBeginBlockers[4+1:]...) // remove evidencetypes.ModuleName - app.mm.SetOrderEndBlockers(crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, + app.ModuleManager.SetOrderEndBlockers(crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, @@ -766,22 +884,27 @@ func NewBabylonApp( ibctransfertypes.ModuleName, zctypes.ModuleName, ibcfeetypes.ModuleName, - wasm.ModuleName, + wasmtypes.ModuleName, + // BTC staking related modules + btcstakingtypes.ModuleName, + finalitytypes.ModuleName, + // tokenomics related modules + incentivetypes.ModuleName, // EndBlock of incentive module does not matter ) // Babylon does not want EndBlock processing in staking - app.mm.OrderEndBlockers = append(app.mm.OrderEndBlockers[:2], app.mm.OrderEndBlockers[2+1:]...) // remove stakingtypes.ModuleName + app.ModuleManager.OrderEndBlockers = append(app.ModuleManager.OrderEndBlockers[:2], app.ModuleManager.OrderEndBlockers[2+1:]...) // remove stakingtypes.ModuleName // NOTE: The genutils module must occur after staking so that pools are // properly initialized with tokens from genesis accounts. // NOTE: Capability module must occur first so that it can initialize any capabilities // so that other modules that want to create or claim capabilities afterwards in InitChain // can do so safely. - app.mm.SetOrderInitGenesis( + genesisModuleOrder := []string{ capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, - paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, + paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, circuittypes.ModuleName, // Babylon modules epochingtypes.ModuleName, btclightclienttypes.ModuleName, @@ -793,17 +916,27 @@ func NewBabylonApp( ibctransfertypes.ModuleName, zctypes.ModuleName, ibcfeetypes.ModuleName, - wasm.ModuleName, - ) + wasmtypes.ModuleName, + // BTC staking related modules + btcstakingtypes.ModuleName, + finalitytypes.ModuleName, + // tokenomics-related modules + incentivetypes.ModuleName, + } + app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) + app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) // Uncomment if you want to set a custom migration order here. // app.mm.SetOrderMigrations(custom order) - app.mm.RegisterInvariants(app.CrisisKeeper) + app.ModuleManager.RegisterInvariants(app.CrisisKeeper) app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) - app.mm.RegisterServices(app.configurator) + err = app.ModuleManager.RegisterServices(app.configurator) + if err != nil { + panic(err) + } - autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.mm.Modules)) + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) reflectionSvc, err := runtimeservices.NewReflectionService() if err != nil { @@ -821,7 +954,7 @@ func NewBabylonApp( overrideModules := map[string]module.AppModuleSimulation{ authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), } - app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager().Modules, overrideModules) + app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) app.sm.RegisterStoreDecoders() @@ -830,10 +963,6 @@ func NewBabylonApp( app.MountTransientStores(tkeys) app.MountMemoryStores(memKeys) - // initialize BaseApp - app.SetInitChainer(app.InitChainer) - app.SetBeginBlocker(app.BeginBlocker) - // initialize AnteHandler, which includes // - authAnteHandler // - custom wasm ante handler NewLimitSimulationGasDecorator and NewCountTXDecorator @@ -846,13 +975,15 @@ func NewBabylonApp( HandlerOptions: ante.HandlerOptions{ AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, - SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SignModeHandler: txConfig.SignModeHandler(), FeegrantKeeper: app.FeeGrantKeeper, SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, - IBCKeeper: app.IBCKeeper, - WasmConfig: &wasmConfig, - TXCounterStoreKey: keys[wasm.StoreKey], + IBCKeeper: app.IBCKeeper, + WasmConfig: &wasmConfig, + TXCounterStoreService: runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), + WasmKeeper: &app.WasmKeeper, + CircuitKeeper: &app.CircuitKeeper, }, ) @@ -865,8 +996,19 @@ func NewBabylonApp( epochingkeeper.NewDropValidatorMsgDecorator(app.EpochingKeeper), NewBtcValidationDecorator(btcConfig, &app.BtcCheckpointKeeper), ) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(anteHandler) + // set postHandler + postHandler := sdk.ChainPostDecorators( + zckeeper.NewIBCHeaderDecorator(app.ZoneConciergeKeeper), + ) + app.SetPostHandler(postHandler) + // must be before Loading version // requires the snapshot store to be created and registered as a BaseAppOption // see cmd/wasmd/root.go: 206 - 214 approx @@ -879,27 +1021,37 @@ func NewBabylonApp( } } - // initialize EndBlocker - app.SetEndBlocker(app.EndBlocker) + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedZoneConciergeKeeper = scopedZoneConciergeKeeper + app.ScopedTransferKeeper = scopedTransferKeeper + app.ScopedWasmKeeper = scopedWasmKeeper + + // At startup, after all modules have been registered, check that all proto + // annotations are correct. + protoFiles, err := proto.MergedRegistry() + if err != nil { + panic(err) + } + err = msgservice.ValidateProtoAnnotations(protoFiles) + if err != nil { + // Once we switch to using protoreflect-based antehandlers, we might + // want to panic here instead of logging a warning. + _, _ = fmt.Fprintln(os.Stderr, err.Error()) + } if loadLatest { if err := app.LoadLatestVersion(); err != nil { - tmos.Exit(err.Error()) + cmtos.Exit(err.Error()) } - ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + ctx := app.BaseApp.NewUncachedContext(true, cmtproto.Header{}) // Initialize pinned codes in wasmvm as they are not persisted there if err := app.WasmKeeper.InitializePinnedCodes(ctx); err != nil { - tmos.Exit(fmt.Sprintf("failed initialize pinned codes %s", err)) + cmtos.Exit(fmt.Sprintf("failed initialize pinned codes %s", err)) } } - app.ScopedIBCKeeper = scopedIBCKeeper - app.ScopedZoneConciergeKeeper = scopedZoneConciergeKeeper - app.ScopedTransferKeeper = scopedTransferKeeper - app.ScopedWasmKeeper = scopedWasmKeeper - return app } @@ -912,24 +1064,42 @@ func (app *BabylonApp) GetBaseApp() *baseapp.BaseApp { // Name returns the name of the App func (app *BabylonApp) Name() string { return app.BaseApp.Name() } +// PreBlocker application updates every pre block +func (app *BabylonApp) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) { + return app.ModuleManager.PreBlock(ctx) +} + // BeginBlocker application updates every begin block -func (app *BabylonApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - return app.mm.BeginBlock(ctx, req) +func (app *BabylonApp) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { + return app.ModuleManager.BeginBlock(ctx) } // EndBlocker application updates every end block -func (app *BabylonApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - return app.mm.EndBlock(ctx, req) +func (app *BabylonApp) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) { + return app.ModuleManager.EndBlock(ctx) } // InitChainer application update at chain initialization -func (app *BabylonApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { +func (app *BabylonApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { var genesisState GenesisState if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) } - app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) - return app.mm.InitGenesis(ctx, app.appCodec, genesisState) + err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()) + if err != nil { + panic(err) + } + + if _, ok := app.ModuleManager.Modules[epochingtypes.ModuleName].(module.HasGenesis); !ok { + panic("FAULTY") + } + + res, err := app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) + if err != nil { + panic(err) + } + + return res, nil } // LoadHeight loads a particular height @@ -968,6 +1138,15 @@ func (app *BabylonApp) InterfaceRegistry() types.InterfaceRegistry { return app.interfaceRegistry } +func (app *BabylonApp) EncodingConfig() *appparams.EncodingConfig { + return &appparams.EncodingConfig{ + InterfaceRegistry: app.InterfaceRegistry(), + Codec: app.AppCodec(), + TxConfig: app.TxConfig(), + Amino: app.LegacyAmino(), + } +} + // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. @@ -1010,13 +1189,13 @@ func (app *BabylonApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.AP authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // Register new tendermint queries routes from grpc-gateway. - tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + cmtservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // Register node gRPC service for grpc-gateway. nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // Register grpc-gateway routes for all modules. - ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + app.BasicModuleManager.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) // register swagger API from root so that other applications can override easily if apiConfig.Swagger { @@ -1031,7 +1210,7 @@ func (app *BabylonApp) RegisterTxService(clientCtx client.Context) { // RegisterTendermintService implements the Application.RegisterTendermintService method. func (app *BabylonApp) RegisterTendermintService(clientCtx client.Context) { - tmservice.RegisterTendermintService( + cmtservice.RegisterTendermintService( clientCtx, app.BaseApp.GRPCQueryRouter(), app.interfaceRegistry, @@ -1039,23 +1218,40 @@ func (app *BabylonApp) RegisterTendermintService(clientCtx client.Context) { ) } -func (app *BabylonApp) RegisterNodeService(clientCtx client.Context) { - nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter()) -} - -func (app *BabylonApp) ModuleManager() *module.Manager { - return app.mm +func (app *BabylonApp) RegisterNodeService(clientCtx client.Context, cfg config.Config) { + nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg) } // DefaultGenesis returns a default genesis from the registered AppModuleBasic's. func (a *BabylonApp) DefaultGenesis() map[string]json.RawMessage { - return ModuleBasics.DefaultGenesis(a.appCodec) + return a.BasicModuleManager.DefaultGenesis(a.appCodec) } func (app *BabylonApp) TxConfig() client.TxConfig { return app.txConfig } +// AutoCliOpts returns the autocli options for the app. +func (app *BabylonApp) AutoCliOpts() autocli.AppOptions { + modules := make(map[string]appmodule.AppModule, 0) + for _, m := range app.ModuleManager.Modules { + if moduleWithName, ok := m.(module.HasName); ok { + moduleName := moduleWithName.Name() + if appModule, ok := moduleWithName.(appmodule.AppModule); ok { + modules[moduleName] = appModule + } + } + } + + return autocli.AppOptions{ + Modules: modules, + ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules), + AddressCodec: authcodec.NewBech32Codec(appparams.Bech32PrefixAccAddr), + ValidatorAddressCodec: authcodec.NewBech32Codec(appparams.Bech32PrefixValAddr), + ConsensusAddressCodec: authcodec.NewBech32Codec(appparams.Bech32PrefixConsAddr), + } +} + // GetMaccPerms returns a copy of the module account permissions func GetMaccPerms() map[string][]string { dupMaccPerms := make(map[string][]string) diff --git a/app/app_test.go b/app/app_test.go index 75a406c64..4e2ac57d0 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -2,28 +2,26 @@ package app import ( "fmt" - "os" "testing" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + abci "github.com/cometbft/cometbft/abci/types" + + "cosmossdk.io/log" + dbm "github.com/cosmos/cosmos-db" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/stretchr/testify/require" ) func TestBabylonBlockedAddrs(t *testing.T) { - encCfg := GetEncodingConfig() db := dbm.NewMemDB() - signer, _ := SetupPrivSigner() - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - app := NewBabyblonAppWithCustomOptions(t, false, signer, SetupOptions{ + signer, _ := SetupTestPrivSigner() + logger := log.NewTestLogger(t) + + app := NewBabylonAppWithCustomOptions(t, false, signer, SetupOptions{ Logger: logger, DB: db, InvCheckPeriod: 0, - EncConfig: encCfg, - HomePath: DefaultNodeHome, SkipUpgradeHeights: map[int64]bool{}, AppOpts: EmptyAppOptions{}, }) @@ -43,12 +41,27 @@ func TestBabylonBlockedAddrs(t *testing.T) { ) } - app.Commit() + _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: 1, + }) + require.NoError(t, err) + _, err = app.Commit() + require.NoError(t, err) - logger2 := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger2 := log.NewTestLogger(t) // Making a new app object with the db, so that initchain hasn't been called - app2 := NewBabylonApp(logger2, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, signer, EmptyAppOptions{}, GetWasmEnabledProposals(), EmptyWasmOpts) - _, err := app2.ExportAppStateAndValidators(false, []string{}, []string{}) + app2 := NewBabylonApp( + logger2, + db, + nil, + true, + map[int64]bool{}, + 0, + signer, + EmptyAppOptions{}, + EmptyWasmOpts, + ) + _, err = app2.ExportAppStateAndValidators(false, []string{}, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } @@ -58,26 +71,24 @@ func TestGetMaccPerms(t *testing.T) { } func TestUpgradeStateOnGenesis(t *testing.T) { - encCfg := GetEncodingConfig() db := dbm.NewMemDB() - privSigner, err := SetupPrivSigner() + privSigner, err := SetupTestPrivSigner() require.NoError(t, err) - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger := log.NewTestLogger(t) - app := NewBabyblonAppWithCustomOptions(t, false, privSigner, SetupOptions{ + app := NewBabylonAppWithCustomOptions(t, false, privSigner, SetupOptions{ Logger: logger, DB: db, InvCheckPeriod: 0, - EncConfig: encCfg, - HomePath: DefaultNodeHome, SkipUpgradeHeights: map[int64]bool{}, AppOpts: EmptyAppOptions{}, }) // make sure the upgrade keeper has version map in state - ctx := app.NewContext(false, tmproto.Header{}) - vm := app.UpgradeKeeper.GetModuleVersionMap(ctx) - for v, i := range app.mm.Modules { + ctx := app.NewContext(false) + vm, err := app.UpgradeKeeper.GetModuleVersionMap(ctx) + require.NoError(t, err) + for v, i := range app.ModuleManager.Modules { if i, ok := i.(module.HasConsensusVersion); ok { require.Equal(t, vm[v], i.ConsensusVersion()) } diff --git a/app/encoding.go b/app/encoding.go index 800f0e302..8aa2a472f 100644 --- a/app/encoding.go +++ b/app/encoding.go @@ -1,23 +1,35 @@ package app import ( - "github.com/cosmos/cosmos-sdk/std" + "cosmossdk.io/log" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + dbm "github.com/cosmos/cosmos-db" appparams "github.com/babylonchain/babylon/app/params" ) -var encodingConfig = makeEncodingConfig() - -func GetEncodingConfig() appparams.EncodingConfig { - return encodingConfig +func NewTmpBabylonApp() *BabylonApp { + signer, _ := SetupTestPrivSigner() + return NewBabylonApp( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + map[int64]bool{}, + 0, + signer, + EmptyAppOptions{}, + []wasmkeeper.Option{}) } -// makeEncodingConfig creates an EncodingConfig. -func makeEncodingConfig() appparams.EncodingConfig { - encodingConfig := appparams.GetEncodingConfig() - std.RegisterLegacyAminoCodec(encodingConfig.Amino) - std.RegisterInterfaces(encodingConfig.InterfaceRegistry) - ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) - ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) - return encodingConfig +// GetEncodingConfig returns a *registered* encoding config +// Note that the only way to register configuration is through the app creation +func GetEncodingConfig() *appparams.EncodingConfig { + tmpApp := NewTmpBabylonApp() + return &appparams.EncodingConfig{ + InterfaceRegistry: tmpApp.InterfaceRegistry(), + Codec: tmpApp.AppCodec(), + TxConfig: tmpApp.TxConfig(), + Amino: tmpApp.LegacyAmino(), + } } diff --git a/app/export.go b/app/export.go index ada8411d7..105c96987 100644 --- a/app/export.go +++ b/app/export.go @@ -4,7 +4,7 @@ import ( "encoding/json" "log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + storetypes "cosmossdk.io/store/types" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,17 +20,20 @@ func (app *BabylonApp) ExportAppStateAndValidators( jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { // as if they could withdraw from the start of the next block - ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctx := app.NewContext(true) // We export at last height + 1, because that's the height at which - // Tendermint will start InitChain. + // Comet will start InitChain. height := app.LastBlockHeight() + 1 if forZeroHeight { height = 0 app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) } - genState := app.mm.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + if err != nil { + return servertypes.ExportedApp{}, err + } appState, err := json.MarshalIndent(genState, "", " ") if err != nil { @@ -73,13 +76,25 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr /* Handle fee distribution state. */ // withdraw all validator commission - app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + err := app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + if err != nil { + panic(err) + } + if _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz); err != nil { + panic(err) + } return false }) + if err != nil { + panic(err) + } // withdraw all delegator rewards - dels := app.StakingKeeper.GetAllDelegations(ctx) + dels, err := app.StakingKeeper.GetAllDelegations(ctx) + if err != nil { + panic(err) + } for _, delegation := range dels { valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) if err != nil { @@ -90,7 +105,9 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr if err != nil { panic(err) } - _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) + if _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr); err != nil { + panic(err) + } } // clear validator slash events @@ -100,20 +117,39 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) // set context height to zero - height := ctx.BlockHeight() - ctx = ctx.WithBlockHeight(0) + height := ctx.HeaderInfo().Height + headerInfo := ctx.HeaderInfo() + headerInfo.Height = 0 + ctx = ctx.WithHeaderInfo(headerInfo) // reinitialize all validators - app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + err = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + if err != nil { + panic(err) + } // donate any unwithdrawn outstanding reward fraction tokens to the community pool - scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) - feePool := app.DistrKeeper.GetFeePool(ctx) + scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz) + if err != nil { + panic(err) + } + feePool, err := app.DistrKeeper.FeePool.Get(ctx) + if err != nil { + panic(err) + } feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) - app.DistrKeeper.SetFeePool(ctx, feePool) + if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil { + panic(err) + } - app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) //nolint:errcheck // either we ignore the error here or propogate up the stack + if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(val.GetOperator())); err != nil { + panic(err) + } return false }) + if err != nil { + panic(err) + } // reinitialize all delegations for _, del := range dels { @@ -121,47 +157,59 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr if err != nil { panic(err) } - delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress) - if err != nil { + delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) + if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { + panic(err) + } + if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { panic(err) } - app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) //nolint:errcheck // either we ignore the error here or propogate up the stack - app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) //nolint:errcheck // either we ignore the error here or propogate up the stack } // reset context height - ctx = ctx.WithBlockHeight(height) + headerInfo = ctx.HeaderInfo() + headerInfo.Height = height + ctx = ctx.WithHeaderInfo(headerInfo) /* Handle staking state. */ // iterate through redelegations, reset creation height - app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + err = app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { for i := range red.Entries { red.Entries[i].CreationHeight = 0 } - app.StakingKeeper.SetRedelegation(ctx, red) + if err := app.StakingKeeper.SetRedelegation(ctx, red); err != nil { + panic(err) + } return false }) + if err != nil { + panic(err) + } // iterate through unbonding delegations, reset creation height - app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + err = app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { for i := range ubd.Entries { ubd.Entries[i].CreationHeight = 0 } - app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + if err := app.StakingKeeper.SetUnbondingDelegation(ctx, ubd); err != nil { + panic(err) + } return false }) + if err != nil { + panic(err) + } // Iterate through validators by power descending, reset bond heights, and // update bond intra-tx counters. store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) - iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) - counter := int16(0) + iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) - validator, found := app.StakingKeeper.GetValidator(ctx, addr) - if !found { + validator, err := app.StakingKeeper.GetValidator(ctx, addr) + if err != nil { panic("expected validator, not found") } @@ -170,13 +218,17 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr validator.Jailed = true } - app.StakingKeeper.SetValidator(ctx, validator) - counter++ + if err := app.StakingKeeper.SetValidator(ctx, validator); err != nil { + panic(err) + } } - iter.Close() + if err := iter.Close(); err != nil { + app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) + return + } - _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) if err != nil { log.Fatal(err) } @@ -184,12 +236,17 @@ func (app *BabylonApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr /* Handle slashing state. */ // reset start height on signing infos - app.SlashingKeeper.IterateValidatorSigningInfos( + err = app.SlashingKeeper.IterateValidatorSigningInfos( ctx, func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { info.StartHeight = 0 - app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + if err := app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info); err != nil { + panic(err) + } return false }, ) + if err != nil { + panic(err) + } } diff --git a/app/genesis.go b/app/genesis.go index 2900679c1..ae974c8e6 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -1,9 +1,11 @@ package app import ( + "cosmossdk.io/log" "encoding/json" - - "github.com/cosmos/cosmos-sdk/codec" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + dbm "github.com/cosmos/cosmos-db" + "testing" ) // GenesisState of the blockchain is represented here as a map of raw json @@ -16,6 +18,19 @@ import ( type GenesisState map[string]json.RawMessage // NewDefaultGenesisState generates the default state for the application. -func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { - return ModuleBasics.DefaultGenesis(cdc) +func NewDefaultGenesisState(t *testing.T) GenesisState { + t.Helper() + // we "pre"-instantiate the application for getting the injected/configured encoding configuration + // note, this is not necessary when using app wiring, as depinject can be directly used (see root_v2.go) + tempApp := NewBabylonApp( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + map[int64]bool{}, + 0, + nil, + EmptyAppOptions{}, + []wasmkeeper.Option{}) + return tempApp.DefaultGenesis() } diff --git a/app/modules.go b/app/modules.go index 424a5179a..b6482870c 100644 --- a/app/modules.go +++ b/app/modules.go @@ -2,9 +2,9 @@ package app import ( "github.com/cosmos/cosmos-sdk/client" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + ibctestingtypes "github.com/cosmos/ibc-go/v8/testing/types" ) // The following functions are required by ibctesting @@ -23,5 +23,5 @@ func (app *BabylonApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { } func (app *BabylonApp) GetTxConfig() client.TxConfig { - return GetEncodingConfig().TxConfig + return app.TxConfig() } diff --git a/app/params/amino.go b/app/params/amino.go deleted file mode 100644 index 9be768342..000000000 --- a/app/params/amino.go +++ /dev/null @@ -1,26 +0,0 @@ -//go:build test_amino -// +build test_amino - -package params - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/types" -) - -// MakeTestEncodingConfig creates an EncodingConfig for an amino based test configuration. -// This function should be used only internally (in the SDK). -// App user should'nt create new codecs - use the app.AppCodec instead. -// [DEPRECATED] -func MakeTestEncodingConfig() EncodingConfig { - cdc := codec.NewLegacyAmino() - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewAminoCodec(cdc) - - return EncodingConfig{ - InterfaceRegistry: interfaceRegistry, - Marshaler: marshaler, - TxConfig: legacytx.StdTxConfig{Cdc: cdc}, - Amino: cdc, - } -} diff --git a/app/params/config.go b/app/params/config.go index 7649b7066..3d4322677 100644 --- a/app/params/config.go +++ b/app/params/config.go @@ -1,6 +1,7 @@ package params import ( + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/types/address" errorsmod "cosmossdk.io/errors" @@ -38,11 +39,11 @@ func init() { } func RegisterDenoms() { - err := sdk.RegisterDenom(HumanCoinUnit, sdk.OneDec()) + err := sdk.RegisterDenom(HumanCoinUnit, math.LegacyOneDec()) if err != nil { panic(err) } - err = sdk.RegisterDenom(BaseCoinUnit, sdk.NewDecWithPrec(1, BbnExponent)) + err = sdk.RegisterDenom(BaseCoinUnit, math.LegacyNewDecWithPrec(1, BbnExponent)) if err != nil { panic(err) } diff --git a/app/params/doc.go b/app/params/doc.go deleted file mode 100644 index bc9d1630a..000000000 --- a/app/params/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Package params defines the simulation parameters in the babylon app. - -It contains the default weights used for each transaction used on the module's -simulation. These weights define the chance for a transaction to be simulated at -any given operation. - -You can replace the default values for the weights by providing a params.json -file with the weights defined for each of the transaction operations: - - { - "op_weight_msg_send": 60, - "op_weight_msg_delegate": 100, - } - -In the example above, the `MsgSend` has 60% chance to be simulated, while the -`MsgDelegate` will always be simulated. -*/ -package params diff --git a/app/params/encoding.go b/app/params/encoding.go index 2cd16263a..8ff9ea04b 100644 --- a/app/params/encoding.go +++ b/app/params/encoding.go @@ -10,8 +10,7 @@ import ( // This is provided for compatibility between protobuf and amino implementations. type EncodingConfig struct { InterfaceRegistry types.InterfaceRegistry - // NOTE: this field will be renamed to Codec - Marshaler codec.Codec - TxConfig client.TxConfig - Amino *codec.LegacyAmino + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino } diff --git a/app/params/params.go b/app/params/params.go deleted file mode 100644 index 85d61e3c1..000000000 --- a/app/params/params.go +++ /dev/null @@ -1,8 +0,0 @@ -package params - -// Simulation parameter constants -const ( - StakePerAccount = "stake_per_account" - InitiallyBondedValidators = "initially_bonded_validators" - SimAppChainID = "simulation-app" -) diff --git a/app/params/proto.go b/app/params/proto.go index f2f36aae3..7ae024c72 100644 --- a/app/params/proto.go +++ b/app/params/proto.go @@ -4,21 +4,39 @@ package params import ( + "github.com/cosmos/gogoproto/proto" + + "cosmossdk.io/x/tx/signing" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/x/auth/tx" ) -// GetEncodingConfig creates an EncodingConfig for an amino based test configuration. -func GetEncodingConfig() EncodingConfig { +// DefaultEncodingConfig returns the default encoding config +func DefaultEncodingConfig() *EncodingConfig { amino := codec.NewLegacyAmino() - interfaceRegistry := types.NewInterfaceRegistry() + interfaceRegistry, err := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{ + ProtoFiles: proto.HybridResolver, + SigningOptions: signing.Options{ + AddressCodec: address.Bech32Codec{ + Bech32Prefix: Bech32PrefixAccAddr, + }, + ValidatorAddressCodec: address.Bech32Codec{ + Bech32Prefix: Bech32PrefixValAddr, + }, + }, + }) + if err != nil { + panic(err) + } marshaler := codec.NewProtoCodec(interfaceRegistry) txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) - return EncodingConfig{ + return &EncodingConfig{ InterfaceRegistry: interfaceRegistry, - Marshaler: marshaler, + Codec: marshaler, TxConfig: txCfg, Amino: amino, } diff --git a/app/params/weights.go b/app/params/weights.go deleted file mode 100644 index 746e304de..000000000 --- a/app/params/weights.go +++ /dev/null @@ -1,28 +0,0 @@ -package params - -// Default simulation operation weights for messages and gov proposals -const ( - DefaultWeightMsgSend int = 100 - DefaultWeightMsgMultiSend int = 10 - DefaultWeightMsgSetWithdrawAddress int = 50 - DefaultWeightMsgWithdrawDelegationReward int = 50 - DefaultWeightMsgWithdrawValidatorCommission int = 50 - DefaultWeightMsgFundCommunityPool int = 50 - DefaultWeightMsgDeposit int = 100 - DefaultWeightMsgVote int = 67 - DefaultWeightMsgVoteWeighted int = 33 - DefaultWeightMsgUnjail int = 100 - DefaultWeightMsgCreateValidator int = 100 - DefaultWeightMsgEditValidator int = 5 - DefaultWeightMsgDelegate int = 100 - DefaultWeightMsgUndelegate int = 100 - DefaultWeightMsgBeginRedelegate int = 100 - - DefaultWeightCommunitySpendProposal int = 5 - DefaultWeightTextProposal int = 5 - DefaultWeightParamChangeProposal int = 5 - - // feegrant - DefaultWeightGrantAllowance int = 100 - DefaultWeightRevokeAllowance int = 100 -) diff --git a/app/test_helpers.go b/app/test_helpers.go index f83e2a392..3a31fa6a4 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -1,49 +1,44 @@ package app import ( - "bytes" - "encoding/hex" "encoding/json" - "fmt" "math/rand" - "strconv" + "os" "testing" "time" + "cosmossdk.io/log" "cosmossdk.io/math" - tmconfig "github.com/cometbft/cometbft/config" - - tmjson "github.com/cometbft/cometbft/libs/json" - - "github.com/babylonchain/babylon/testutil/datagen" - - errorsmod "cosmossdk.io/errors" - "github.com/babylonchain/babylon/app/params" - appparams "github.com/babylonchain/babylon/app/params" - bbn "github.com/babylonchain/babylon/types" - dbm "github.com/cometbft/cometbft-db" + pruningtypes "cosmossdk.io/store/pruning/types" abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/crypto/ed25519" + tmjson "github.com/cometbft/cometbft/libs/json" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - sec256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/types" simsutils "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/mempool" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/docker/docker/pkg/ioutils" "github.com/stretchr/testify/require" + + appparams "github.com/babylonchain/babylon/app/params" + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/privval" + bbn "github.com/babylonchain/babylon/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" ) // SetupOptions defines arguments that are passed into `Simapp` constructor. @@ -51,46 +46,64 @@ type SetupOptions struct { Logger log.Logger DB *dbm.MemDB InvCheckPeriod uint - HomePath string SkipUpgradeHeights map[int64]bool - EncConfig params.EncodingConfig AppOpts types.AppOptions } -func setup(withGenesis bool, invCheckPeriod uint) (*BabylonApp, GenesisState) { +func setup(t *testing.T, ps *PrivSigner, withGenesis bool, invCheckPeriod uint) (*BabylonApp, GenesisState) { db := dbm.NewMemDB() - encCdc := GetEncodingConfig() - privSigner, err := SetupPrivSigner() - if err != nil { - panic(err) - } - app := NewBabylonApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, privSigner, EmptyAppOptions{}, GetWasmEnabledProposals(), EmptyWasmOpts) + nodeHome := t.TempDir() + + appOptions := make(simsutils.AppOptionsMap, 0) + appOptions[flags.FlagHome] = nodeHome // ensure unique folder + appOptions[server.FlagInvCheckPeriod] = invCheckPeriod + appOptions["btc-config.network"] = string(bbn.BtcSimnet) + appOptions[server.FlagPruning] = pruningtypes.PruningOptionDefault + appOptions[server.FlagMempoolMaxTxs] = mempool.DefaultMaxTx + appOptions[flags.FlagChainID] = "chain-test" + baseAppOpts := server.DefaultBaseappOptions(appOptions) + app := NewBabylonApp( + log.NewNopLogger(), + db, + nil, + true, + map[int64]bool{}, + invCheckPeriod, + ps, + appOptions, + EmptyWasmOpts, + baseAppOpts..., + ) if withGenesis { - return app, NewDefaultGenesisState(encCdc.Marshaler) + return app, app.DefaultGenesis() } return app, GenesisState{} } -// NewBabyblonAppWithCustomOptions initializes a new BabylonApp with custom options. -// Created Babylon application will have one validator with hardoced amout of tokens. +// NewBabylonAppWithCustomOptions initializes a new BabylonApp with custom options. +// Created Babylon application will have one validator with hardcoed amout of tokens. // This is necessary as from cosmos-sdk 0.46 it is required that there is at least // one validator in validator set during InitGenesis abci call - https://github.com/cosmos/cosmos-sdk/pull/9697 -func NewBabyblonAppWithCustomOptions(t *testing.T, isCheckTx bool, privSigner *PrivSigner, options SetupOptions) *BabylonApp { +func NewBabylonAppWithCustomOptions(t *testing.T, isCheckTx bool, privSigner *PrivSigner, options SetupOptions) *BabylonApp { t.Helper() - privVal := datagen.NewPV() - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - - // generate genesis account - senderPrivKey := sec256k1.GenPrivKey() + valKeys, err := privval.NewValidatorKeys(ed25519.GenPrivKey(), bls12381.GenPrivKey()) + require.NoError(t, err) + valPubkey, err := cryptocodec.FromCmtPubKeyInterface(valKeys.ValPubkey) + require.NoError(t, err) + genesisKey, err := checkpointingtypes.NewGenesisKey( + sdk.ValAddress(valKeys.ValPubkey.Address()), + &valKeys.BlsPubkey, + valKeys.PoP, + &cosmosed.PubKey{Key: valPubkey.Bytes()}, + ) + require.NoError(t, err) + genesisValSet := []*checkpointingtypes.GenesisKey{genesisKey} - acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + acc := authtypes.NewBaseAccount(valPubkey.Address().Bytes(), valPubkey, 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, math.NewInt(100000000000000))), } app := NewBabylonApp( @@ -99,13 +112,13 @@ func NewBabyblonAppWithCustomOptions(t *testing.T, isCheckTx bool, privSigner *P nil, true, options.SkipUpgradeHeights, - options.HomePath, options.InvCheckPeriod, - options.EncConfig, privSigner, - options.AppOpts, GetWasmEnabledProposals(), EmptyWasmOpts) - genesisState := NewDefaultGenesisState(app.appCodec) - genesisState = genesisStateWithValSet(t, app, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) + options.AppOpts, + EmptyWasmOpts, + ) + genesisState := app.DefaultGenesis() + genesisState = genesisStateWithValSet(t, app, genesisState, genesisValSet, []authtypes.GenesisAccount{acc}, balance) if !isCheckTx { // init chain must be called to stop deliverState from being nil @@ -113,13 +126,18 @@ func NewBabyblonAppWithCustomOptions(t *testing.T, isCheckTx bool, privSigner *P require.NoError(t, err) // Initialize the chain - app.InitChain( - abci.RequestInitChain{ + consensusParams := simsutils.DefaultConsensusParams + initialHeight := app.LastBlockHeight() + 1 + consensusParams.Abci = &cmtproto.ABCIParams{VoteExtensionsEnableHeight: initialHeight} + _, err = app.InitChain( + &abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, - ConsensusParams: simsutils.DefaultConsensusParams, + ConsensusParams: consensusParams, AppStateBytes: stateBytes, + InitialHeight: initialHeight, }, ) + require.NoError(t, err) } return app @@ -127,59 +145,67 @@ func NewBabyblonAppWithCustomOptions(t *testing.T, isCheckTx bool, privSigner *P func genesisStateWithValSet(t *testing.T, app *BabylonApp, genesisState GenesisState, - valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, + valSet []*checkpointingtypes.GenesisKey, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance, ) GenesisState { // set genesis accounts authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) - delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + validators := make([]stakingtypes.Validator, 0, len(valSet)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet)) - bondAmt := sdk.DefaultPowerReduction + bondAmt := sdk.DefaultPowerReduction.MulRaw(1000) - for _, val := range valSet.Validators { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) - require.NoError(t, err) - pkAny, err := codectypes.NewAnyWithValue(pk) + for _, valGenKey := range valSet { + pkAny, err := codectypes.NewAnyWithValue(valGenKey.ValPubkey) require.NoError(t, err) validator := stakingtypes.Validator{ - OperatorAddress: sdk.ValAddress(val.Address).String(), + OperatorAddress: valGenKey.ValidatorAddress, ConsensusPubkey: pkAny, Jailed: false, Status: stakingtypes.Bonded, Tokens: bondAmt, - DelegatorShares: sdk.OneDec(), + DelegatorShares: math.LegacyOneDec(), Description: stakingtypes.Description{}, UnbondingHeight: int64(0), UnbondingTime: time.Unix(0, 0).UTC(), - Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - MinSelfDelegation: sdk.ZeroInt(), + Commission: stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), + MinSelfDelegation: math.ZeroInt(), } - validators = append(validators, validator) - delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress().String(), valGenKey.ValidatorAddress, math.LegacyOneDec())) + // blsKeys = append(blsKeys, checkpointingtypes.NewGenesisKey(sdk.ValAddress(val.Address), genesisBLSPubkey)) } + // total bond amount = bond amount * number of validators + require.Equal(t, len(validators), len(delegations)) + totalBondAmt := bondAmt.MulRaw(int64(len(validators))) + // set validators and delegations stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + stakingGenesis.Params.BondDenom = appparams.DefaultBondDenom genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) + checkpointingGenesis := &checkpointingtypes.GenesisState{ + GenesisKeys: valSet, + } + genesisState[checkpointingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(checkpointingGenesis) + totalSupply := sdk.NewCoins() for _, b := range balances { // add genesis acc tokens to total supply totalSupply = totalSupply.Add(b.Coins...) } - for range delegations { // add delegated tokens to total supply - totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) + totalSupply = totalSupply.Add(sdk.NewCoin(appparams.DefaultBondDenom, bondAmt)) } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + Coins: sdk.Coins{sdk.NewCoin(appparams.DefaultBondDenom, totalBondAmt)}, }) // update total supply @@ -202,37 +228,37 @@ func genesisStateWithValSet(t *testing.T, func Setup(t *testing.T, isCheckTx bool) *BabylonApp { t.Helper() - privVal := datagen.NewPV() - pubKey, err := privVal.GetPubKey() + ps, err := SetupTestPrivSigner() require.NoError(t, err) - - // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - + valPubKey := ps.WrappedPV.Key.PubKey // generate genesis account - senderPrivKey := sec256k1.GenPrivKey() - acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + acc := authtypes.NewBaseAccount(valPubKey.Address().Bytes(), &cosmosed.PubKey{Key: valPubKey.Bytes()}, 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, math.NewInt(100000000000000))), } + ps.WrappedPV.Key.DelegatorAddress = acc.GetAddress().String() + // create validator set with single validator + genesisKey, err := GenesisKeyFromPrivSigner(ps) + require.NoError(t, err) + genesisValSet := []*checkpointingtypes.GenesisKey{genesisKey} - app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + app := SetupWithGenesisValSet(t, genesisValSet, ps, []authtypes.GenesisAccount{acc}, balance) return app } -// SetupPrivSigner sets up a PrivSigner for testing -func SetupPrivSigner() (*PrivSigner, error) { - kr, err := client.NewKeyringFromBackend(client.Context{}, keyring.BackendMemory) +// SetupTestPrivSigner sets up a PrivSigner for testing +func SetupTestPrivSigner() (*PrivSigner, error) { + // Create a temporary node directory + nodeDir, err := ioutils.TempDir("", "tmp-signer") if err != nil { return nil, err } - nodeCfg := tmconfig.DefaultConfig() - encodingCfg := appparams.GetEncodingConfig() - privSigner, _ := InitPrivSigner(client.Context{}, ".", kr, "", encodingCfg) - privSigner.WrappedPV.Clean(nodeCfg.PrivValidatorKeyFile(), nodeCfg.PrivValidatorStateFile()) + defer func() { + _ = os.RemoveAll(nodeDir) + }() + privSigner, _ := InitPrivSigner(nodeDir) return privSigner, nil } @@ -240,128 +266,57 @@ func SetupPrivSigner() (*PrivSigner, error) { // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit (10^6) in the default token of the babylon app from first genesis // account. A Nop logger is set in BabylonApp. -func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *BabylonApp { - app, genesisState := setup(true, 5) - // set genesis accounts - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - - validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) - delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) - - // sdk.DefaultPowerReduction is 1 unit of voting power, which by default needs 1000000 tokens - bondAmt := sdk.DefaultPowerReduction.MulRaw(1000) - - for _, val := range valSet.Validators { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) - require.NoError(t, err) - pkAny, err := codectypes.NewAnyWithValue(pk) - require.NoError(t, err) - validator := stakingtypes.Validator{ - OperatorAddress: sdk.ValAddress(val.Address).String(), - ConsensusPubkey: pkAny, - Jailed: false, - Status: stakingtypes.Bonded, - Tokens: bondAmt, - DelegatorShares: sdk.OneDec(), - Description: stakingtypes.Description{}, - UnbondingHeight: int64(0), - UnbondingTime: time.Unix(0, 0).UTC(), - Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - MinSelfDelegation: sdk.ZeroInt(), - } - validators = append(validators, validator) - delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) - } - - // total bond amount = bond amount * number of validators - require.Equal(t, len(validators), len(delegations)) - totalBondAmt := bondAmt.MulRaw(int64(len(validators))) - - // set validators and delegations - stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) - stakingGenesis.Params.BondDenom = appparams.DefaultBondDenom - genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) - - totalSupply := sdk.NewCoins() - for _, b := range balances { - // add genesis acc tokens and delegated tokens to total supply - totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(appparams.DefaultBondDenom, totalBondAmt))...) - } - - // add bonded amount to bonded pool module account - balances = append(balances, banktypes.Balance{ - Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(appparams.DefaultBondDenom, totalBondAmt)}, - }) - - // update total supply - bankGenesis := banktypes.NewGenesisState( - banktypes.DefaultGenesisState().Params, - balances, - totalSupply, - []banktypes.Metadata{}, - []banktypes.SendEnabled{}, - ) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) +// Note that the privSigner should be the 0th item of valSet +func SetupWithGenesisValSet(t *testing.T, valSet []*checkpointingtypes.GenesisKey, privSigner *PrivSigner, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *BabylonApp { + t.Helper() + app, genesisState := setup(t, privSigner, true, 5) + genesisState = genesisStateWithValSet(t, app, genesisState, valSet, genAccs, balances...) stateBytes, err := json.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // init chain will set the validator set and initialize the genesis accounts - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: simsutils.DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) + consensusParams := simsutils.DefaultConsensusParams + consensusParams.Block.MaxGas = 100 * simsutils.DefaultGenTxGas + // it is required that the VoteExtensionsEnableHeight > 0 to enable vote extension + initialHeight := app.LastBlockHeight() + 1 + consensusParams.Abci = &cmtproto.ABCIParams{VoteExtensionsEnableHeight: initialHeight} + _, err = app.InitChain(&abci.RequestInitChain{ + ChainId: app.ChainID(), + Time: time.Now().UTC(), + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: consensusParams, + InitialHeight: initialHeight, + AppStateBytes: stateBytes, + }) + require.NoError(t, err) + + _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: initialHeight, + Hash: app.LastCommitID().Hash, + }) + require.NoError(t, err) return app } -// SetupWithGenesisAccounts initializes a new BabylonApp with the provided genesis -// accounts and possible balances. -func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *BabylonApp { - app, genesisState := setup(true, 0) - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - - totalSupply := sdk.NewCoins() - for _, b := range balances { - totalSupply = totalSupply.Add(b.Coins...) +func GenesisKeyFromPrivSigner(ps *PrivSigner) (*checkpointingtypes.GenesisKey, error) { + valKeys, err := privval.NewValidatorKeys(ps.WrappedPV.GetValPrivKey(), ps.WrappedPV.GetBlsPrivKey()) + if err != nil { + return nil, err } - - bankGenesis := banktypes.NewGenesisState( - banktypes.DefaultGenesisState().Params, - balances, - totalSupply, - []banktypes.Metadata{}, - []banktypes.SendEnabled{}, - ) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) - - stateBytes, err := json.MarshalIndent(genesisState, "", " ") + valPubkey, err := cryptocodec.FromCmtPubKeyInterface(valKeys.ValPubkey) if err != nil { - panic(err) + return nil, err } - - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: simsutils.DefaultConsensusParams, - AppStateBytes: stateBytes, - }, + return checkpointingtypes.NewGenesisKey( + ps.WrappedPV.GetAddress(), + &valKeys.BlsPubkey, + valKeys.PoP, + &cosmosed.PubKey{Key: valPubkey.Bytes()}, ) - - app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) - - return app } -type GenerateAccountStrategy func(int) []sdk.AccAddress - // createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. func createRandomAccounts(accNum int) []sdk.AccAddress { testAddrs := make([]sdk.AccAddress, accNum) @@ -373,59 +328,22 @@ func createRandomAccounts(accNum int) []sdk.AccAddress { return testAddrs } -// createIncrementalAccounts is a strategy used by addTestAddrs() in order to generated addresses in ascending order. -func createIncrementalAccounts(accNum int) []sdk.AccAddress { - var addresses []sdk.AccAddress - var buffer bytes.Buffer - - // start at 100 so we can make up to 999 test addresses with valid test addresses - for i := 100; i < (accNum + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") // base address string - - buffer.WriteString(numString) // adding on final two digits to make addresses unique - res, _ := sdk.AccAddressFromHexUnsafe(buffer.String()) - bech := res.String() - addr, _ := TestAddr(buffer.String(), bech) - - addresses = append(addresses, addr) - buffer.Reset() - } - - return addresses -} - -// AddTestAddrsFromPubKeys adds the addresses into the BabylonApp providing only the public keys. -func AddTestAddrsFromPubKeys(app *BabylonApp, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt math.Int) { - initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - - for _, pk := range pubKeys { - initAccountWithCoins(app, ctx, sdk.AccAddress(pk.Address()), initCoins) - } -} - // AddTestAddrs constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order -func AddTestAddrs(app *BabylonApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { - return addTestAddrs(app, ctx, accNum, accAmt, createRandomAccounts) -} +func AddTestAddrs(app *BabylonApp, ctx sdk.Context, accNum int, accAmt math.Int) ([]sdk.AccAddress, error) { + testAddrs := createRandomAccounts(accNum) -// AddTestAddrs constructs and returns accNum amount of accounts with an -// initial balance of accAmt in random order -func AddTestAddrsIncremental(app *BabylonApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { - return addTestAddrs(app, ctx, accNum, accAmt, createIncrementalAccounts) -} - -func addTestAddrs(app *BabylonApp, ctx sdk.Context, accNum int, accAmt math.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { - testAddrs := strategy(accNum) - - initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) + bondDenom, err := app.StakingKeeper.BondDenom(ctx) + if err != nil { + return nil, err + } + initCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, accAmt)) for _, addr := range testAddrs { initAccountWithCoins(app, ctx, addr, initCoins) } - return testAddrs + return testAddrs, nil } func initAccountWithCoins(app *BabylonApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { @@ -440,58 +358,27 @@ func initAccountWithCoins(app *BabylonApp, ctx sdk.Context, addr sdk.AccAddress, } } -// ConvertAddrsToValAddrs converts the provided addresses to ValAddress. -func ConvertAddrsToValAddrs(addrs []sdk.AccAddress) []sdk.ValAddress { - valAddrs := make([]sdk.ValAddress, len(addrs)) - - for i, addr := range addrs { - valAddrs[i] = sdk.ValAddress(addr) - } - - return valAddrs -} +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} -func TestAddr(addr string, bech string) (sdk.AccAddress, error) { - res, err := sdk.AccAddressFromHexUnsafe(addr) - if err != nil { - return nil, err - } - bechexpected := res.String() - if bech != bechexpected { - return nil, fmt.Errorf("bech encoding doesn't match reference") - } +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + // some defaults required for app.toml config - bechres, err := sdk.AccAddressFromBech32(bech) - if err != nil { - return nil, err - } - if !bytes.Equal(bechres, res) { - return nil, err + if o == "btc-config.network" { + return string(bbn.BtcSimnet) } - return res, nil -} - -// CheckBalance checks the balance of an account. -func CheckBalance(t *testing.T, app *BabylonApp, addr sdk.AccAddress, balances sdk.Coins) { - ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) - require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr))) + return nil } -// SignCheckDeliver checks a generated signed transaction and simulates a -// block commitment with the given transaction. A test assertion is made using -// the parameter 'expPass' against the result. A corresponding result is -// returned. -func SignCheckDeliver( - t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, - chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, -) (sdk.GasInfo, *sdk.Result, error) { - r := rand.New(rand.NewSource(time.Now().UnixNano())) +// SignAndDeliverWithoutCommit signs and delivers a transaction. No commit +func SignAndDeliverWithoutCommit(t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, msgs []sdk.Msg, fees sdk.Coins, chainID string, accNums, accSeqs []uint64, blockTime time.Time, priv ...cryptotypes.PrivKey) (*abci.ResponseFinalizeBlock, error) { tx, err := simsutils.GenSignedMockTx( - r, + rand.New(rand.NewSource(time.Now().UnixNano())), txCfg, msgs, - sdk.Coins{sdk.NewInt64Coin(appparams.DefaultBondDenom, 0)}, + fees, simsutils.DefaultGenTxGas, chainID, accNums, @@ -499,139 +386,14 @@ func SignCheckDeliver( priv..., ) require.NoError(t, err) - txBytes, err := txCfg.TxEncoder()(tx) - require.Nil(t, err) - - // Must simulate now as CheckTx doesn't run Msgs anymore - _, res, err := app.Simulate(txBytes) - - if expSimPass { - require.NoError(t, err) - require.NotNil(t, res) - } else { - require.Error(t, err) - require.Nil(t, res) - } - - // Simulate a sending a transaction and committing a block - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - gInfo, res, err := app.SimDeliver(txCfg.TxEncoder(), tx) - - if expPass { - require.NoError(t, err) - require.NotNil(t, res) - } else { - require.Error(t, err) - require.Nil(t, res) - } - - app.EndBlock(abci.RequestEndBlock{}) - app.Commit() - - return gInfo, res, err -} - -// GenSequenceOfTxs generates a set of signed transactions of messages, such -// that they differ only by having the sequence numbers incremented between -// every transaction. -func GenSequenceOfTxs(txGen client.TxConfig, msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...cryptotypes.PrivKey) ([]sdk.Tx, error) { - txs := make([]sdk.Tx, numToGenerate) - var err error - for i := 0; i < numToGenerate; i++ { - r := rand.New(rand.NewSource(time.Now().UnixNano())) - txs[i], err = simsutils.GenSignedMockTx( - r, - txGen, - msgs, - sdk.Coins{sdk.NewInt64Coin(appparams.DefaultBondDenom, 0)}, - simsutils.DefaultGenTxGas, - "", - accNums, - initSeqNums, - priv..., - ) - if err != nil { - break - } - incrementAllSequenceNumbers(initSeqNums) - } - - return txs, err -} - -func incrementAllSequenceNumbers(initSeqNums []uint64) { - for i := 0; i < len(initSeqNums); i++ { - initSeqNums[i]++ - } -} - -// CreateTestPubKeys returns a total of numPubKeys public keys in ascending order. -func CreateTestPubKeys(numPubKeys int) []cryptotypes.PubKey { - var publicKeys []cryptotypes.PubKey - var buffer bytes.Buffer - - // start at 10 to avoid changing 1 to 01, 2 to 02, etc - for i := 100; i < (numPubKeys + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") // base pubkey string - buffer.WriteString(numString) // adding on final two digits to make pubkeys unique - publicKeys = append(publicKeys, NewPubKeyFromHex(buffer.String())) - buffer.Reset() - } - - return publicKeys -} -// NewPubKeyFromHex returns a PubKey from a hex string. -func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - if len(pkBytes) != ed25519.PubKeySize { - panic(errorsmod.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")) - } - return &ed25519.PubKey{Key: pkBytes} -} - -// EmptyAppOptions is a stub implementing AppOptions -type EmptyAppOptions struct{} - -// Get implements AppOptions -func (ao EmptyAppOptions) Get(o string) interface{} { - // some defaults required for app.toml config - - if o == "btc-config.network" { - return string(bbn.BtcSimnet) - } - - return nil -} - -// FundAccount is a utility function that funds an account by minting and -// sending the coins to the address. This should be used for testing purposes -// only! -// -// TODO: Instead of using the mint module account, which has the -// permission of minting, create a "faucet" account. (@fdymylja) -func FundAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { - if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { - return err - } - - return bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) -} - -// FundModuleAccount is a utility function that funds a module account by -// minting and sending the coins to the address. This should be used for testing -// purposes only! -// -// TODO: Instead of using the mint module account, which has the -// permission of minting, create a "faucet" account. (@fdymylja) -func FundModuleAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, recipientMod string, amounts sdk.Coins) error { - if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { - return err - } + bz, err := txCfg.TxEncoder()(tx) + require.NoError(t, err) - return bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, recipientMod, amounts) + return app.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: app.LastBlockHeight() + 1, + Hash: app.LastCommitID().Hash, + Time: blockTime, + Txs: [][]byte{bz}, + }) } diff --git a/app/types.go b/app/types.go deleted file mode 100644 index fc631ccdc..000000000 --- a/app/types.go +++ /dev/null @@ -1,44 +0,0 @@ -package app - -import ( - abci "github.com/cometbft/cometbft/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" -) - -// App implements the common methods for a Cosmos SDK-based application -// specific blockchain. -type App interface { - // The assigned name of the app. - Name() string - - // The application types codec. - // NOTE: This shoult be sealed before being returned. - LegacyAmino() *codec.LegacyAmino - - // Application updates every begin block. - BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock - - // Application updates every end block. - EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock - - // Application update at chain (i.e app) initialization. - InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain - - // Loads the app at a given height. - LoadHeight(height int64) error - - // Exports the state of the application for a genesis file. - ExportAppStateAndValidators( - forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string, - ) (types.ExportedApp, error) - - // All the registered module account addreses. - ModuleAccountAddrs() map[string]bool - - // Helper for the simulation framework. - SimulationManager() *module.SimulationManager -} diff --git a/app/utils.go b/app/utils.go index 4bbd2fd41..8869582bf 100644 --- a/app/utils.go +++ b/app/utils.go @@ -7,15 +7,10 @@ import ( "path/filepath" "text/template" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - - tmconfig "github.com/cometbft/cometbft/config" - tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cosmos/cosmos-sdk/client" + cmtconfig "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client/config" - "github.com/cosmos/cosmos-sdk/x/auth/types" - appparams "github.com/babylonchain/babylon/app/params" "github.com/babylonchain/babylon/privval" ) @@ -40,51 +35,24 @@ broadcast-mode = "{{ .BroadcastMode }}" type PrivSigner struct { WrappedPV *privval.WrappedFilePV - ClientCtx client.Context } -func InitPrivSigner(clientCtx client.Context, nodeDir string, kr keyring.Keyring, feePayer string, encodingCfg appparams.EncodingConfig) (*PrivSigner, error) { - // setup private validator - nodeCfg := tmconfig.DefaultConfig() +func InitPrivSigner(nodeDir string) (*PrivSigner, error) { + nodeCfg := cmtconfig.DefaultConfig() pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) - err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777) + err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) if err != nil { return nil, err } pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - err = tmos.EnsureDir(filepath.Dir(pvStateFile), 0777) + err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) if err != nil { return nil, err } wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) - clientCtx = clientCtx. - WithInterfaceRegistry(encodingCfg.InterfaceRegistry). - WithCodec(encodingCfg.Marshaler). - WithLegacyAmino(encodingCfg.Amino). - WithTxConfig(encodingCfg.TxConfig). - WithAccountRetriever(types.AccountRetriever{}). - WithSkipConfirmation(true). - WithKeyring(kr). - WithNodeURI(nodeCfg.RPC.ListenAddress) - - if feePayer != "" { - feePayerRecord, err := kr.Key(feePayer) - if err != nil { - return nil, err - } - feePayerAddress, err := feePayerRecord.GetAddress() - if err != nil { - return nil, err - } - clientCtx = clientCtx. - WithFromAddress(feePayerAddress). - WithFromName(feePayer) - } - return &PrivSigner{ WrappedPV: wrappedPV, - ClientCtx: clientCtx, }, nil } diff --git a/btcstaking/btcstaking_test.go b/btcstaking/btcstaking_test.go new file mode 100644 index 000000000..22f0df961 --- /dev/null +++ b/btcstaking/btcstaking_test.go @@ -0,0 +1,637 @@ +package btcstaking_test + +import ( + "bytes" + "math/rand" + "sort" + "testing" + "time" + + "github.com/babylonchain/babylon/btcstaking" + btctest "github.com/babylonchain/babylon/testutil/bitcoin" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +type TestScenario struct { + StakerKey *btcec.PrivateKey + FinalityProviderKeys []*btcec.PrivateKey + CovenantKeys []*btcec.PrivateKey + RequiredCovenantSigs uint32 + StakingAmount btcutil.Amount + StakingTime uint16 +} + +func GenerateTestScenario( + r *rand.Rand, + t *testing.T, + numFinalityProviderKeys uint32, + numCovenantKeys uint32, + requiredCovenantSigs uint32, + stakingAmount btcutil.Amount, + stakingTime uint16, +) *TestScenario { + stakerPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + finalityProviderKeys := make([]*btcec.PrivateKey, numFinalityProviderKeys) + for i := uint32(0); i < numFinalityProviderKeys; i++ { + covenantPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + finalityProviderKeys[i] = covenantPrivKey + } + + covenantKeys := make([]*btcec.PrivateKey, numCovenantKeys) + + for i := uint32(0); i < numCovenantKeys; i++ { + covenantPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + covenantKeys[i] = covenantPrivKey + } + + return &TestScenario{ + StakerKey: stakerPrivKey, + FinalityProviderKeys: finalityProviderKeys, + CovenantKeys: covenantKeys, + RequiredCovenantSigs: requiredCovenantSigs, + StakingAmount: stakingAmount, + StakingTime: stakingTime, + } +} + +func (t *TestScenario) CovenantPublicKeys() []*btcec.PublicKey { + covenantPubKeys := make([]*btcec.PublicKey, len(t.CovenantKeys)) + + for i, covenantKey := range t.CovenantKeys { + covenantPubKeys[i] = covenantKey.PubKey() + } + + return covenantPubKeys +} + +func (t *TestScenario) FinalityProviderPublicKeys() []*btcec.PublicKey { + finalityProviderPubKeys := make([]*btcec.PublicKey, len(t.FinalityProviderKeys)) + + for i, fpKey := range t.FinalityProviderKeys { + finalityProviderPubKeys[i] = fpKey.PubKey() + } + + return finalityProviderPubKeys +} + +func TestSpendingTimeLockPath(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + scenario := GenerateTestScenario( + r, + t, + 1, + 5, + 3, + btcutil.Amount(2*10e8), + 5, + ) + + stakingInfo, err := btcstaking.BuildStakingInfo( + scenario.StakerKey.PubKey(), + scenario.FinalityProviderPublicKeys(), + scenario.CovenantPublicKeys(), + scenario.RequiredCovenantSigs, + scenario.StakingTime, + scenario.StakingAmount, + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(scenario.StakingAmount.MulF64(0.5)), + }, + ) + + // to spend tx as staker, we need to set the sequence number to be >= stakingTimeBlocks + spendStakeTx.TxIn[0].Sequence = uint32(scenario.StakingTime) + + si, err := stakingInfo.TimeLockPathSpendInfo() + require.NoError(t, err) + + sig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.StakerKey, + si.RevealedLeaf, + ) + + require.NoError(t, err) + + witness, err := si.CreateTimeLockPathWitness(sig) + require.NoError(t, err) + + spendStakeTx.TxIn[0].Witness = witness + + prevOutputFetcher := stakingInfo.GetOutputFetcher() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + stakingInfo.GetPkScript(), + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), stakingInfo.StakingOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) +} + +type SignatureInfo struct { + SignerPubKey *btcec.PublicKey + Signature *schnorr.Signature +} + +func NewSignatureInfo( + signerPubKey *btcec.PublicKey, + signature *schnorr.Signature, +) *SignatureInfo { + return &SignatureInfo{ + SignerPubKey: signerPubKey, + Signature: signature, + } +} + +// Helper function to sort all signatures in reverse lexicographical order of signing public keys +// this way signatures are ready to be used in multisig witness with corresponding public keys +func sortSignatureInfo(infos []*SignatureInfo) []*SignatureInfo { + sortedInfos := make([]*SignatureInfo, len(infos)) + copy(sortedInfos, infos) + sort.SliceStable(sortedInfos, func(i, j int) bool { + keyIBytes := schnorr.SerializePubKey(sortedInfos[i].SignerPubKey) + keyJBytes := schnorr.SerializePubKey(sortedInfos[j].SignerPubKey) + return bytes.Compare(keyIBytes, keyJBytes) == 1 + }) + + return sortedInfos +} + +// generate list of signatures in valid order +func GenerateSignatures( + t *testing.T, + keys []*btcec.PrivateKey, + tx *wire.MsgTx, + stakingOutput *wire.TxOut, + leaf txscript.TapLeaf, +) []*schnorr.Signature { + + var si []*SignatureInfo + + for _, key := range keys { + pubKey := key.PubKey() + sig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + tx, + stakingOutput, + key, + leaf, + ) + require.NoError(t, err) + info := NewSignatureInfo( + pubKey, + sig, + ) + si = append(si, info) + } + + // sort signatures by public key + sortedSigInfo := sortSignatureInfo(si) + + var sigs []*schnorr.Signature = make([]*schnorr.Signature, len(sortedSigInfo)) + + for i, sigInfo := range sortedSigInfo { + sig := sigInfo + sigs[i] = sig.Signature + } + + return sigs +} + +func TestSpendingUnbondingPathCovenant35MultiSig(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + + // we are having here 3/5 covenant threshold sig + scenario := GenerateTestScenario( + r, + t, + 1, + 5, + 3, + btcutil.Amount(2*10e8), + 5, + ) + + stakingInfo, err := btcstaking.BuildStakingInfo( + scenario.StakerKey.PubKey(), + scenario.FinalityProviderPublicKeys(), + scenario.CovenantPublicKeys(), + scenario.RequiredCovenantSigs, + scenario.StakingTime, + scenario.StakingAmount, + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(scenario.StakingAmount.MulF64(0.5)), + }, + ) + + si, err := stakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + + stakerSig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.StakerKey, + si.RevealedLeaf, + ) + + require.NoError(t, err) + + // scenario where all keys are available + covenantSigantures := GenerateSignatures( + t, + scenario.CovenantKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + witness, err := si.CreateUnbondingPathWitness(covenantSigantures, stakerSig) + require.NoError(t, err) + spendStakeTx.TxIn[0].Witness = witness + + prevOutputFetcher := stakingInfo.GetOutputFetcher() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + stakingInfo.GetPkScript(), + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), stakingInfo.StakingOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) + + numOfCovenantMembers := len(scenario.CovenantKeys) + // with each loop iteration we remove one key from the list of signatures + for i := 0; i < numOfCovenantMembers; i++ { + numOfRemovedSignatures := i + 1 + + covenantSigantures := GenerateSignatures( + t, + scenario.CovenantKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + + for j := 0; j <= i; j++ { + // NOTE: Number provides signatures must match number of public keys in the script, + // if we are missing some signatures those must be set to empty signature in witness + covenantSigantures[j] = nil + } + + witness, err := si.CreateUnbondingPathWitness(covenantSigantures, stakerSig) + require.NoError(t, err) + spendStakeTx.TxIn[0].Witness = witness + + if numOfCovenantMembers-numOfRemovedSignatures >= int(scenario.RequiredCovenantSigs) { + // if we are above threshold execution should be successful + btctest.AssertEngineExecution(t, 0, true, newEngine) + } else { + // we are below threshold execution should be unsuccessful + btctest.AssertEngineExecution(t, 0, false, newEngine) + } + } +} + +func TestSpendingUnbondingPathSingleKeyCovenant(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + + // generate single key covenant + scenario := GenerateTestScenario( + r, + t, + 1, + 1, + 1, + btcutil.Amount(2*10e8), + 5, + ) + + stakingInfo, err := btcstaking.BuildStakingInfo( + scenario.StakerKey.PubKey(), + scenario.FinalityProviderPublicKeys(), + scenario.CovenantPublicKeys(), + scenario.RequiredCovenantSigs, + scenario.StakingTime, + scenario.StakingAmount, + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(scenario.StakingAmount.MulF64(0.5)), + }, + ) + + si, err := stakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + + stakerSig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.StakerKey, + si.RevealedLeaf, + ) + require.NoError(t, err) + + // scenario where all keys are available + covenantSigantures := GenerateSignatures( + t, + scenario.CovenantKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + witness, err := si.CreateUnbondingPathWitness(covenantSigantures, stakerSig) + require.NoError(t, err) + spendStakeTx.TxIn[0].Witness = witness + + prevOutputFetcher := stakingInfo.GetOutputFetcher() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + stakingInfo.GetPkScript(), + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), stakingInfo.StakingOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) +} + +func TestSpendingSlashingPathCovenant35MultiSig(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + + // we are having here 3/5 covenant threshold sig + scenario := GenerateTestScenario( + r, + t, + 1, + 5, + 3, + btcutil.Amount(2*10e8), + 5, + ) + + stakingInfo, err := btcstaking.BuildStakingInfo( + scenario.StakerKey.PubKey(), + scenario.FinalityProviderPublicKeys(), + scenario.CovenantPublicKeys(), + scenario.RequiredCovenantSigs, + scenario.StakingTime, + scenario.StakingAmount, + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(scenario.StakingAmount.MulF64(0.5)), + }, + ) + + si, err := stakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + // generate staker signature, covenant signatures, and finality provider signature + stakerSig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.StakerKey, + si.RevealedLeaf, + ) + require.NoError(t, err) + covenantSigantures := GenerateSignatures( + t, + scenario.CovenantKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + fpSig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.FinalityProviderKeys[0], + si.RevealedLeaf, + ) + require.NoError(t, err) + + witness, err := si.CreateSlashingPathWitness( + covenantSigantures, + []*schnorr.Signature{fpSig}, + stakerSig, + ) + require.NoError(t, err) + spendStakeTx.TxIn[0].Witness = witness + + // now as we have finality provider signature execution should succeed + prevOutputFetcher := stakingInfo.GetOutputFetcher() + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + stakingInfo.GetPkScript(), + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), stakingInfo.StakingOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) +} + +func TestSpendingSlashingPathCovenant35MultiSigFinalityProviderRestaking(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + + // we have 3 out of 5 covenant committee, and we are restaking to 2 finality providers + scenario := GenerateTestScenario( + r, + t, + 2, + 5, + 3, + btcutil.Amount(2*10e8), + 5, + ) + + stakingInfo, err := btcstaking.BuildStakingInfo( + scenario.StakerKey.PubKey(), + scenario.FinalityProviderPublicKeys(), + scenario.CovenantPublicKeys(), + scenario.RequiredCovenantSigs, + scenario.StakingTime, + scenario.StakingAmount, + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(scenario.StakingAmount.MulF64(0.5)), + }, + ) + + si, err := stakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + // generate staker signature, covenant signatures, and finality provider signature + stakerSig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + stakingInfo.StakingOutput, + scenario.StakerKey, + si.RevealedLeaf, + ) + require.NoError(t, err) + + // only use 3 out of 5 covenant signatures + covenantSigantures := GenerateSignatures( + t, + scenario.CovenantKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + covenantSigantures[0] = nil + covenantSigantures[1] = nil + + // only use one of the finality provider signatures + // script should still be valid as we require only one finality provider signature + // to be present + fpSignatures := GenerateSignatures( + t, + scenario.FinalityProviderKeys, + spendStakeTx, + stakingInfo.StakingOutput, + si.RevealedLeaf, + ) + fpSignatures[0] = nil + + witness, err := si.CreateSlashingPathWitness(covenantSigantures, fpSignatures, stakerSig) + require.NoError(t, err) + spendStakeTx.TxIn[0].Witness = witness + + prevOutputFetcher := stakingInfo.GetOutputFetcher() + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + stakingInfo.GetPkScript(), + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), stakingInfo.StakingOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) +} + +func TestSpendingRelativeTimeLockScript(t *testing.T) { + stakerPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + stakerPubKey := stakerPrivKey.PubKey() + lockTime := uint16(10) + lockedAmount := btcutil.Amount(2 * 10e8) + + // to spend output with relative timelock transaction need to be version two or higher + spendStakeTx := wire.NewMsgTx(2) + spendStakeTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + spendStakeTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + // spend half of the staking amount + Value: int64(lockedAmount.MulF64(0.5)), + }, + ) + + tls, err := btcstaking.BuildRelativeTimelockTaprootScript( + stakerPubKey, + lockTime, + &chaincfg.MainNetParams, + ) + require.NoError(t, err) + + timeLockOutput := &wire.TxOut{ + PkScript: tls.PkScript, + Value: int64(lockedAmount), + } + + // we need to set sequence number before signing, as signing commits to sequence + // number + spendStakeTx.TxIn[0].Sequence = uint32(tls.LockTime) + + sig, err := btcstaking.SignTxWithOneScriptSpendInputFromTapLeaf( + spendStakeTx, + timeLockOutput, + stakerPrivKey, + tls.SpendInfo.RevealedLeaf, + ) + + require.NoError(t, err) + + witness, err := btcstaking.CreateWitness( + tls.SpendInfo, + [][]byte{sig.Serialize()}, + ) + + require.NoError(t, err) + + spendStakeTx.TxIn[0].Witness = witness + + prevOutputFetcher := txscript.NewCannedPrevOutputFetcher( + timeLockOutput.PkScript, timeLockOutput.Value, + ) + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + timeLockOutput.PkScript, + spendStakeTx, 0, txscript.StandardVerifyFlags, nil, + txscript.NewTxSigHashes(spendStakeTx, prevOutputFetcher), timeLockOutput.Value, + prevOutputFetcher, + ) + } + btctest.AssertEngineExecution(t, 0, true, newEngine) +} diff --git a/btcstaking/errors.go b/btcstaking/errors.go new file mode 100644 index 000000000..af1625f3a --- /dev/null +++ b/btcstaking/errors.go @@ -0,0 +1,10 @@ +package btcstaking + +import "errors" + +var ( + ErrInvalidSlashingRate = errors.New("invalid slashing rate") + ErrDustOutputFound = errors.New("transaction contains a dust output") + ErrInsufficientSlashingAmount = errors.New("insufficient slashing amount") + ErrInsufficientChangeAmount = errors.New("insufficient change amount") +) diff --git a/btcstaking/scripts_utils.go b/btcstaking/scripts_utils.go new file mode 100644 index 000000000..e9e2a2928 --- /dev/null +++ b/btcstaking/scripts_utils.go @@ -0,0 +1,134 @@ +package btcstaking + +import ( + "bytes" + "fmt" + "sort" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/txscript" +) + +// private helper to assemble multisig script +// SCRIPT: OP_CHEKCSIG OP_CHECKSIGADD OP_CHECKSIGADD ... OP_CHECKSIGADD OP_GREATERTHANOREQUAL OP_VERIFY +func assembleMultiSigScript( + pubkeys []*btcec.PublicKey, + threshold uint32, + withVerify bool, +) ([]byte, error) { + builder := txscript.NewScriptBuilder() + + for i, key := range pubkeys { + builder.AddData(schnorr.SerializePubKey(key)) + if i == 0 { + builder.AddOp(txscript.OP_CHECKSIG) + } else { + builder.AddOp(txscript.OP_CHECKSIGADD) + } + } + + builder.AddInt64(int64(threshold)) + builder.AddOp(txscript.OP_GREATERTHANOREQUAL) + if withVerify { + builder.AddOp(txscript.OP_VERIFY) + } + + return builder.Script() +} + +// sortKeys takes a set of schnorr public keys and returns a new slice that is +// a copy of the keys sorted in lexicographical order bytes on the x-only +// pubkey serialization. +func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey { + sort.SliceStable(keys, func(i, j int) bool { + keyIBytes := schnorr.SerializePubKey(keys[i]) + keyJBytes := schnorr.SerializePubKey(keys[j]) + return bytes.Compare(keyIBytes, keyJBytes) == -1 + }) + return keys +} + +// prepareKeys prepares keys to be used in multisig script +// Validates: +// - whether there are at lest 2 keys +// - whether there are no duplicate keys +// returns copy of the slice of keys sorted lexicographically +func prepareKeysForMultisigScript(keys []*btcec.PublicKey) ([]*btcec.PublicKey, error) { + if len(keys) < 2 { + return nil, fmt.Errorf("cannot create multisig script with less than 2 keys") + } + + sortedKeys := sortKeys(keys) + + for i := 0; i < len(sortedKeys)-1; i++ { + if bytes.Equal(schnorr.SerializePubKey(sortedKeys[i]), schnorr.SerializePubKey(sortedKeys[i+1])) { + return nil, fmt.Errorf("duplicate key in list of keys") + } + } + + return sortedKeys, nil +} + +// buildMultiSigScript creates multisig script with given keys and signer threshold to +// successfully execute script +// it validates whether provided keys are unique and the threshold is not greater than number of keys +// If there is only one key provided it will return single key sig script +func buildMultiSigScript( + keys []*btcec.PublicKey, + threshold uint32, + withVerify bool, +) ([]byte, error) { + if len(keys) == 0 { + return nil, fmt.Errorf("no keys provided") + } + + if threshold > uint32(len(keys)) { + return nil, fmt.Errorf("required number of valid signers is greater than number of provided keys") + } + + if len(keys) == 1 { + // if we have only one key we can use single key sig script + return buildSingleKeySigScript(keys[0], withVerify) + } + + sortedKeys, err := prepareKeysForMultisigScript(keys) + + if err != nil { + return nil, err + } + + return assembleMultiSigScript(sortedKeys, threshold, withVerify) +} + +// Only holder of private key for given pubKey can spend after relative lock time +// SCRIPT: OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY +func buildTimeLockScript( + pubKey *btcec.PublicKey, + lockTime uint16, +) ([]byte, error) { + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(pubKey)) + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + builder.AddInt64(int64(lockTime)) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + return builder.Script() +} + +// Only holder of private key for given pubKey can spend +// SCRIPT: OP_CHECKSIGVERIFY +func buildSingleKeySigScript( + pubKey *btcec.PublicKey, + withVerify bool, +) ([]byte, error) { + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(pubKey)) + + if withVerify { + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + } else { + builder.AddOp(txscript.OP_CHECKSIG) + } + + return builder.Script() +} diff --git a/btcstaking/staking.go b/btcstaking/staking.go new file mode 100644 index 000000000..47a224298 --- /dev/null +++ b/btcstaking/staking.go @@ -0,0 +1,687 @@ +package btcstaking + +import ( + "bytes" + "encoding/hex" + "fmt" + + sdkmath "cosmossdk.io/math" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/mempool" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" +) + +// buildSlashingTxFromOutpoint builds a valid slashing transaction by creating a new Bitcoin transaction that slashes a portion +// of staked funds and directs them to a specified slashing address. The transaction also includes a change output sent back to +// the specified change address. The slashing rate determines the proportion of staked funds to be slashed. +// +// Parameters: +// - stakingOutput: The staking output to be spent in the transaction. +// - stakingAmount: The amount of staked funds in the staking output. +// - fee: The transaction fee to be paid. +// - slashingAddress: The Bitcoin address to which the slashed funds will be sent. +// - changeAddress: The Bitcoin address to receive the change from the transaction. +// - slashingRate: The rate at which the staked funds will be slashed, expressed as a decimal. +// +// Returns: +// - *wire.MsgTx: The constructed slashing transaction without a script signature or witness. +// - error: An error if any validation or construction step fails. +func buildSlashingTxFromOutpoint( + stakingOutput wire.OutPoint, + stakingAmount, fee int64, + slashingAddress, changeAddress btcutil.Address, + slashingRate sdkmath.LegacyDec, +) (*wire.MsgTx, error) { + // Validate staking amount + if stakingAmount <= 0 { + return nil, fmt.Errorf("staking amount must be larger than 0") + } + + // Validate slashing rate + if !IsSlashingRateValid(slashingRate) { + return nil, ErrInvalidSlashingRate + } + + // Calculate the amount to be slashed + slashingRateFloat64, err := slashingRate.Float64() + if err != nil { + return nil, fmt.Errorf("error converting slashing rate to float64: %w", err) + } + slashingAmount := btcutil.Amount(stakingAmount).MulF64(slashingRateFloat64) + if slashingAmount <= 0 { + return nil, ErrInsufficientSlashingAmount + } + // Generate script for slashing address + slashingAddrScript, err := txscript.PayToAddrScript(slashingAddress) + if err != nil { + return nil, err + } + + // Calculate the change amount + changeAmount := btcutil.Amount(stakingAmount) - slashingAmount - btcutil.Amount(fee) + if changeAmount <= 0 { + return nil, ErrInsufficientChangeAmount + } + // Generate script for change address + changeAddrScript, err := txscript.PayToAddrScript(changeAddress) + if err != nil { + return nil, err + } + + // Create a new btc transaction + tx := wire.NewMsgTx(wire.TxVersion) + // TODO: this builds input with sequence number equal to MaxTxInSequenceNum, which + // means this tx is not replacable. + input := wire.NewTxIn(&stakingOutput, nil, nil) + tx.AddTxIn(input) + tx.AddTxOut(wire.NewTxOut(int64(slashingAmount), slashingAddrScript)) + tx.AddTxOut(wire.NewTxOut(int64(changeAmount), changeAddrScript)) + + // Verify that the none of the outputs is a dust output. + for _, out := range tx.TxOut { + if mempool.IsDust(out, mempool.DefaultMinRelayTxFee) { + return nil, ErrDustOutputFound + } + } + + return tx, nil +} + +func getPossibleStakingOutput( + stakingTx *wire.MsgTx, + stakingOutputIdx uint32, +) (*wire.TxOut, error) { + if stakingTx == nil { + return nil, fmt.Errorf("provided staking transaction must not be nil") + } + + if stakingOutputIdx >= uint32(len(stakingTx.TxOut)) { + return nil, fmt.Errorf("invalid staking output index %d, tx has %d outputs", stakingOutputIdx, len(stakingTx.TxOut)) + } + + stakingOutput := stakingTx.TxOut[stakingOutputIdx] + + if !txscript.IsPayToTaproot(stakingOutput.PkScript) { + return nil, fmt.Errorf("must be pay to taproot output") + } + + return stakingOutput, nil +} + +// BuildSlashingTxFromStakingTxStrict constructs a valid slashing transaction using information from a staking transaction, +// a specified staking output index, and additional parameters such as slashing and change addresses, transaction fee, +// staking script, script version, and network. This function performs stricter validation compared to BuildSlashingTxFromStakingTx. +// +// Parameters: +// - stakingTx: The staking transaction from which the staking output is to be used for slashing. +// - stakingOutputIdx: The index of the staking output in the staking transaction. +// - slashingAddress: The Bitcoin address to which the slashed funds will be sent. +// - stakerPk: public key of the staker i.e the btc holder who can spend staking output after lock time +// - slashingChangeLockTime: lock time which will be used on slashing transaction change output +// - fee: The transaction fee to be paid. +// - slashingRate: The rate at which the staked funds will be slashed, expressed as a decimal. +// - script: The staking script to which the staking output should commit. +// - net: The network on which transactions should take place (e.g., mainnet, testnet). +// +// Returns: +// - *wire.MsgTx: The constructed slashing transaction without script signature or witness. +// - error: An error if any validation or construction step fails. +// +// This function validates the same conditions as BuildSlashingTxFromStakingTx and additionally checks whether the +// staking output at the specified index commits to the provided script and whether the provided script is a valid +// staking script for the given network. If any of these additional validations fail, an error is returned. +func BuildSlashingTxFromStakingTxStrict( + stakingTx *wire.MsgTx, + stakingOutputIdx uint32, + slashingAddress btcutil.Address, + stakerPk *btcec.PublicKey, + slashChangeLockTime uint16, + fee int64, + slashingRate sdkmath.LegacyDec, + net *chaincfg.Params, +) (*wire.MsgTx, error) { + // Get the staking output at the specified index from the staking transaction + stakingOutput, err := getPossibleStakingOutput(stakingTx, stakingOutputIdx) + if err != nil { + return nil, err + } + + // Create an OutPoint for the staking output + stakingTxHash := stakingTx.TxHash() + stakingOutpoint := wire.NewOutPoint(&stakingTxHash, stakingOutputIdx) + + // Create taproot address commiting to timelock script + si, err := BuildRelativeTimelockTaprootScript( + stakerPk, + slashChangeLockTime, + net, + ) + + if err != nil { + return nil, err + } + + // Build slashing tx with the staking output information + return buildSlashingTxFromOutpoint( + *stakingOutpoint, + stakingOutput.Value, fee, + slashingAddress, si.TapAddress, + slashingRate) +} + +// IsTransferTx Transfer transaction is a transaction which: +// - has exactly one input +// - has exactly one output +func IsTransferTx(tx *wire.MsgTx) error { + if tx == nil { + return fmt.Errorf("transfer transaction must have cannot be nil") + } + + if len(tx.TxIn) != 1 { + return fmt.Errorf("transfer transaction must have exactly one input") + } + + if len(tx.TxOut) != 1 { + return fmt.Errorf("transfer transaction must have exactly one output") + } + + return nil +} + +// IsSimpleTransfer Simple transfer transaction is a transaction which: +// - has exactly one input +// - has exactly one output +// - is not replacable +// - does not have any locktime +func IsSimpleTransfer(tx *wire.MsgTx) error { + if err := IsTransferTx(tx); err != nil { + return fmt.Errorf("invalid simple tansfer tx: %w", err) + } + + if tx.TxIn[0].Sequence != wire.MaxTxInSequenceNum { + return fmt.Errorf("simple transfer tx must not be replacable") + } + + if tx.LockTime != 0 { + return fmt.Errorf("simple transfer tx must not have locktime") + } + return nil +} + +// ValidateSlashingTx performs basic checks on a slashing transaction: +// - the slashing transaction is not nil. +// - the slashing transaction has exactly one input. +// - the slashing transaction is non-replaceable. +// - the lock time of the slashing transaction is 0. +// - the slashing transaction has exactly two outputs, and: +// - the first output must pay to the provided slashing address. +// - the first output must pay at least (staking output value * slashing rate) to the slashing address. +// - neither of the outputs are considered dust. +// +// - the min fee for slashing tx is preserved +func ValidateSlashingTx( + slashingTx *wire.MsgTx, + slashingAddress btcutil.Address, + slashingRate sdkmath.LegacyDec, + slashingTxMinFee, stakingOutputValue int64, + stakerPk *btcec.PublicKey, + slashingChangeLockTime uint16, + net *chaincfg.Params, +) error { + // Verify that the slashing transaction is not nil. + if slashingTx == nil { + return fmt.Errorf("slashing transaction must not be nil") + } + + // Verify that the slashing transaction has exactly one input. + if len(slashingTx.TxIn) != 1 { + return fmt.Errorf("slashing transaction must have exactly one input") + } + + // Verify that the slashing transaction is non-replaceable. + if slashingTx.TxIn[0].Sequence != wire.MaxTxInSequenceNum { + return fmt.Errorf("slashing transaction must not be replaceable") + } + + // Verify that lock time of the slashing transaction is 0. + if slashingTx.LockTime != 0 { + return fmt.Errorf("slashing tx must not have locktime") + } + + // Verify that the slashing transaction has exactly two outputs. + if len(slashingTx.TxOut) != 2 { + return fmt.Errorf("slashing transaction must have exactly 2 outputs") + } + + // Verify that at least staking output value * slashing rate is slashed. + slashingRateFloat64, err := slashingRate.Float64() + if err != nil { + return fmt.Errorf("error converting slashing rate to float64: %w", err) + } + minSlashingAmount := btcutil.Amount(stakingOutputValue).MulF64(slashingRateFloat64) + if btcutil.Amount(slashingTx.TxOut[0].Value) < minSlashingAmount { + return fmt.Errorf("slashing transaction must slash at least staking output value * slashing rate") + } + + // Verify that the first output pays to the provided slashing address. + slashingPkScript, err := txscript.PayToAddrScript(slashingAddress) + if err != nil { + return fmt.Errorf("error creating slashing pk script: %w", err) + } + if !bytes.Equal(slashingTx.TxOut[0].PkScript, slashingPkScript) { + return fmt.Errorf("slashing transaction must pay to the provided slashing address") + } + + // Verify that the second output pays to the taproot address which locks funds for + // slashingChangeLockTime + si, err := BuildRelativeTimelockTaprootScript( + stakerPk, + slashingChangeLockTime, + net, + ) + + if err != nil { + return fmt.Errorf("error creating change timelock script: %w", err) + } + + if !bytes.Equal(slashingTx.TxOut[1].PkScript, si.PkScript) { + return fmt.Errorf("invalid slashing tx change output pkscript, expected: %s, got: %s", hex.EncodeToString(si.PkScript), hex.EncodeToString(slashingTx.TxOut[1].PkScript)) + } + + // Verify that the none of the outputs is a dust output. + for _, out := range slashingTx.TxOut { + if mempool.IsDust(out, mempool.DefaultMinRelayTxFee) { + return ErrDustOutputFound + } + } + + /* + Check Fees + */ + // Check that values of slashing and staking transaction are larger than 0 + if slashingTx.TxOut[0].Value <= 0 || stakingOutputValue <= 0 { + return fmt.Errorf("values of slashing and staking transaction must be larger than 0") + } + + // Calculate the sum of output values in the slashing transaction. + slashingTxOutSum := int64(0) + for _, out := range slashingTx.TxOut { + slashingTxOutSum += out.Value + } + + // Ensure that the staking transaction value is larger than the sum of slashing transaction output values. + if stakingOutputValue <= slashingTxOutSum { + return fmt.Errorf("slashing transaction must not spend more than staking transaction") + } + + // Ensure that the slashing transaction fee is larger than the specified minimum fee. + if stakingOutputValue-slashingTxOutSum < slashingTxMinFee { + return fmt.Errorf("slashing transaction fee must be larger than %d", slashingTxMinFee) + } + + return nil +} + +// CheckTransactions validates all relevant data of slashing and funding transaction. +// - funding transaction has output committing to the provided script +// - slashing transaction is valid +// - slashing transaction input hash is pointing to funding transaction hash +// - slashing transaction input index is pointing to funding transaction output commiting to the script +func CheckTransactions( + slashingTx *wire.MsgTx, + fundingTransaction *wire.MsgTx, + fundingOutputIdx uint32, + slashingTxMinFee int64, + slashingRate sdkmath.LegacyDec, + slashingAddress btcutil.Address, + stakerPk *btcec.PublicKey, + slashingChangeLockTime uint16, + net *chaincfg.Params, +) error { + // Check if slashing tx min fee is valid + if slashingTxMinFee <= 0 { + return fmt.Errorf("slashing transaction min fee must be larger than 0") + } + + // Check if slashing rate is in the valid range (0,1) + if !IsSlashingRateValid(slashingRate) { + return ErrInvalidSlashingRate + } + + if fundingOutputIdx >= uint32(len(fundingTransaction.TxOut)) { + return fmt.Errorf("invalid funding output index %d, tx has %d outputs", fundingOutputIdx, len(fundingTransaction.TxOut)) + } + + stakingOutput := fundingTransaction.TxOut[fundingOutputIdx] + // 3. Check if slashing transaction is valid + if err := ValidateSlashingTx( + slashingTx, + slashingAddress, + slashingRate, + slashingTxMinFee, + stakingOutput.Value, + stakerPk, + slashingChangeLockTime, + net); err != nil { + return err + } + + // 4. Check that slashing transaction input is pointing to staking transaction + stakingTxHash := fundingTransaction.TxHash() + if !slashingTx.TxIn[0].PreviousOutPoint.Hash.IsEqual(&stakingTxHash) { + return fmt.Errorf("slashing transaction must spend staking output") + } + + // 5. Check that index of the fund output matches index of the input in slashing transaction + if slashingTx.TxIn[0].PreviousOutPoint.Index != fundingOutputIdx { + return fmt.Errorf("slashing transaction input must spend staking output") + } + return nil +} + +func signTxWithOneScriptSpendInputFromTapLeafInternal( + txToSign *wire.MsgTx, + fundingOutput *wire.TxOut, + privKey *btcec.PrivateKey, + tapLeaf txscript.TapLeaf) (*schnorr.Signature, error) { + + inputFetcher := txscript.NewCannedPrevOutputFetcher( + fundingOutput.PkScript, + fundingOutput.Value, + ) + + sigHashes := txscript.NewTxSigHashes(txToSign, inputFetcher) + + sig, err := txscript.RawTxInTapscriptSignature( + txToSign, sigHashes, 0, fundingOutput.Value, + fundingOutput.PkScript, tapLeaf, txscript.SigHashDefault, + privKey, + ) + + if err != nil { + return nil, err + } + + parsedSig, err := schnorr.ParseSignature(sig) + + if err != nil { + return nil, err + } + + return parsedSig, nil +} + +// SignTxWithOneScriptSpendInputFromTapLeaf signs transaction with one input coming +// from script spend output. +// It does not do any validations, expect that txToSign has exactly one input. +func SignTxWithOneScriptSpendInputFromTapLeaf( + txToSign *wire.MsgTx, + fundingOutput *wire.TxOut, + privKey *btcec.PrivateKey, + tapLeaf txscript.TapLeaf, +) (*schnorr.Signature, error) { + if txToSign == nil { + return nil, fmt.Errorf("tx to sign must not be nil") + } + + if fundingOutput == nil { + return nil, fmt.Errorf("funding output must not be nil") + } + + if privKey == nil { + return nil, fmt.Errorf("private key must not be nil") + } + + if len(txToSign.TxIn) != 1 { + return nil, fmt.Errorf("tx to sign must have exactly one input") + } + + return signTxWithOneScriptSpendInputFromTapLeafInternal( + txToSign, + fundingOutput, + privKey, + tapLeaf, + ) +} + +// SignTxWithOneScriptSpendInputFromScript signs transaction with one input coming +// from script spend output with provided script. +// It does not do any validations, expect that txToSign has exactly one input. +func SignTxWithOneScriptSpendInputFromScript( + txToSign *wire.MsgTx, + fundingOutput *wire.TxOut, + privKey *btcec.PrivateKey, + script []byte, +) (*schnorr.Signature, error) { + tapLeaf := txscript.NewBaseTapLeaf(script) + return SignTxWithOneScriptSpendInputFromTapLeaf(txToSign, fundingOutput, privKey, tapLeaf) +} + +// SignTxWithOneScriptSpendInputStrict signs transaction with one input coming +// from script spend output with provided script. +// It checks: +// - txToSign is not nil +// - txToSign has exactly one input +// - fundingTx is not nil +// - fundingTx has one output committing to the provided script +// - txToSign input is pointing to the correct output in fundingTx +func SignTxWithOneScriptSpendInputStrict( + txToSign *wire.MsgTx, + fundingTx *wire.MsgTx, + fundingOutputIdx uint32, + signedScriptPath []byte, + privKey *btcec.PrivateKey, +) (*schnorr.Signature, error) { + + if err := checkTxBeforeSigning(txToSign, fundingTx, fundingOutputIdx); err != nil { + return nil, fmt.Errorf("invalid tx: %w", err) + } + + fundingOutput := fundingTx.TxOut[fundingOutputIdx] + + return SignTxWithOneScriptSpendInputFromScript(txToSign, fundingOutput, privKey, signedScriptPath) +} + +// EncSignTxWithOneScriptSpendInputStrict is encrypted version of +// SignTxWithOneScriptSpendInputStrict with the output to be encrypted +// by an encryption key (adaptor signature) +func EncSignTxWithOneScriptSpendInputStrict( + txToSign *wire.MsgTx, + fundingTx *wire.MsgTx, + fundingOutputIdx uint32, + signedScriptPath []byte, + privKey *btcec.PrivateKey, + encKey *asig.EncryptionKey, +) (*asig.AdaptorSignature, error) { + + if err := checkTxBeforeSigning(txToSign, fundingTx, fundingOutputIdx); err != nil { + return nil, fmt.Errorf("invalid tx: %w", err) + } + + fundingOutput := fundingTx.TxOut[fundingOutputIdx] + + tapLeaf := txscript.NewBaseTapLeaf(signedScriptPath) + + inputFetcher := txscript.NewCannedPrevOutputFetcher( + fundingOutput.PkScript, + fundingOutput.Value, + ) + + sigHashes := txscript.NewTxSigHashes(txToSign, inputFetcher) + + sigHash, err := txscript.CalcTapscriptSignaturehash( + sigHashes, + txscript.SigHashDefault, + txToSign, + 0, + inputFetcher, + tapLeaf) + if err != nil { + return nil, err + } + + adaptorSig, err := asig.EncSign(privKey, encKey, sigHash) + if err != nil { + return nil, err + } + + return adaptorSig, nil +} + +func checkTxBeforeSigning(txToSign *wire.MsgTx, fundingTx *wire.MsgTx, fundingOutputIdx uint32) error { + if txToSign == nil { + return fmt.Errorf("tx to sign must not be nil") + } + + if len(txToSign.TxIn) != 1 { + return fmt.Errorf("tx to sign must have exactly one input") + } + + if fundingOutputIdx >= uint32(len(fundingTx.TxOut)) { + return fmt.Errorf("invalid funding output index %d, tx has %d outputs", fundingOutputIdx, len(fundingTx.TxOut)) + } + + fundingTxHash := fundingTx.TxHash() + + if !txToSign.TxIn[0].PreviousOutPoint.Hash.IsEqual(&fundingTxHash) { + return fmt.Errorf("txToSign must input point to fundingTx") + } + + if txToSign.TxIn[0].PreviousOutPoint.Index != fundingOutputIdx { + return fmt.Errorf("txToSign inpunt index must point to output with provided script") + } + + return nil +} + +// VerifyTransactionSigWithOutput verifies that: +// - provided transaction has exactly one input +// - provided signature is valid schnorr BIP340 signature +// - provided signature is signing whole provided transaction (SigHashDefault) +func VerifyTransactionSigWithOutput( + transaction *wire.MsgTx, + fundingOutput *wire.TxOut, + script []byte, + pubKey *btcec.PublicKey, + signature []byte) error { + + if fundingOutput == nil { + return fmt.Errorf("funding output must not be nil") + } + + return VerifyTransactionSigWithOutputData( + transaction, + fundingOutput.PkScript, + fundingOutput.Value, + script, + pubKey, + signature, + ) +} + +// VerifyTransactionSigWithOutputData verifies that: +// - provided transaction has exactly one input +// - provided signature is valid schnorr BIP340 signature +// - provided signature is signing whole provided transaction (SigHashDefault) +func VerifyTransactionSigWithOutputData( + transaction *wire.MsgTx, + fundingOutputPkScript []byte, + fundingOutputValue int64, + script []byte, + pubKey *btcec.PublicKey, + signature []byte) error { + + if transaction == nil { + return fmt.Errorf("tx to verify not be nil") + } + + if len(transaction.TxIn) != 1 { + return fmt.Errorf("tx to sign must have exactly one input") + } + + if pubKey == nil { + return fmt.Errorf("public key must not be nil") + } + + tapLeaf := txscript.NewBaseTapLeaf(script) + + inputFetcher := txscript.NewCannedPrevOutputFetcher( + fundingOutputPkScript, + fundingOutputValue, + ) + + sigHashes := txscript.NewTxSigHashes(transaction, inputFetcher) + + sigHash, err := txscript.CalcTapscriptSignaturehash( + sigHashes, txscript.SigHashDefault, transaction, 0, inputFetcher, tapLeaf, + ) + + if err != nil { + return err + } + + parsedSig, err := schnorr.ParseSignature(signature) + + if err != nil { + return err + } + + valid := parsedSig.Verify(sigHash, pubKey) + + if !valid { + return fmt.Errorf("signature is not valid") + } + + return nil +} + +// EncVerifyTransactionSigWithOutputData verifies that: +// - provided transaction has exactly one input +// - provided signature is valid adaptor signature +// - provided signature is signing whole provided transaction (SigHashDefault) +func EncVerifyTransactionSigWithOutputData( + transaction *wire.MsgTx, + fundingOutputPkScript []byte, + fundingOutputValue int64, + script []byte, + pubKey *btcec.PublicKey, + encKey *asig.EncryptionKey, + signature *asig.AdaptorSignature, +) error { + if transaction == nil { + return fmt.Errorf("tx to verify not be nil") + } + + if len(transaction.TxIn) != 1 { + return fmt.Errorf("tx to sign must have exactly one input") + } + + if pubKey == nil { + return fmt.Errorf("public key must not be nil") + } + + tapLeaf := txscript.NewBaseTapLeaf(script) + + inputFetcher := txscript.NewCannedPrevOutputFetcher( + fundingOutputPkScript, + fundingOutputValue, + ) + + sigHashes := txscript.NewTxSigHashes(transaction, inputFetcher) + + sigHash, err := txscript.CalcTapscriptSignaturehash( + sigHashes, txscript.SigHashDefault, transaction, 0, inputFetcher, tapLeaf, + ) + + if err != nil { + return err + } + + return signature.EncVerify(pubKey, encKey, sigHash) +} diff --git a/btcstaking/staking_test.go b/btcstaking/staking_test.go new file mode 100644 index 000000000..1837bf719 --- /dev/null +++ b/btcstaking/staking_test.go @@ -0,0 +1,242 @@ +package btcstaking_test + +import ( + "fmt" + "math" + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/btcstaking" + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/mempool" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +// StakingScriptData is a struct that holds data parsed from staking script +type StakingScriptData struct { + StakerKey *btcec.PublicKey + FinalityProviderKey *btcec.PublicKey + CovenantKey *btcec.PublicKey + StakingTime uint16 +} + +func NewStakingScriptData( + stakerKey, + fpKey, + covenantKey *btcec.PublicKey, + stakingTime uint16) (*StakingScriptData, error) { + + if stakerKey == nil || fpKey == nil || covenantKey == nil { + return nil, fmt.Errorf("staker, finality provider and covenant keys cannot be nil") + } + + return &StakingScriptData{ + StakerKey: stakerKey, + FinalityProviderKey: fpKey, + CovenantKey: covenantKey, + StakingTime: stakingTime, + }, nil +} + +func genValidStakingScriptData(_ *testing.T, r *rand.Rand) *StakingScriptData { + stakerPrivKeyBytes := datagen.GenRandomByteArray(r, 32) + fpPrivKeyBytes := datagen.GenRandomByteArray(r, 32) + covenantPrivKeyBytes := datagen.GenRandomByteArray(r, 32) + stakingTime := uint16(r.Intn(math.MaxUint16)) + + _, stakerPublicKey := btcec.PrivKeyFromBytes(stakerPrivKeyBytes) + _, fpPublicKey := btcec.PrivKeyFromBytes(fpPrivKeyBytes) + _, covenantPublicKey := btcec.PrivKeyFromBytes(covenantPrivKeyBytes) + + sd, _ := NewStakingScriptData(stakerPublicKey, fpPublicKey, covenantPublicKey, stakingTime) + + return sd +} + +func FuzzGeneratingValidStakingSlashingTx(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // we do not care for inputs in staking tx + stakingTx := wire.NewMsgTx(2) + stakingOutputIdx := r.Intn(5) + // always more outputs than stakingOutputIdx + stakingTxNumOutputs := r.Intn(5) + 10 + slashingLockTime := uint16(r.Intn(1000) + 1) + sd := genValidStakingScriptData(t, r) + + minStakingValue := 5000 + minFee := 2000 + // generate a random slashing rate with random precision, + // this will include both valid and invalid ranges, so we can test both cases + randomPrecision := r.Int63n(4) // [0,3] + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 1001)), randomPrecision) // [0,1000] / 10^{randomPrecision} + + for i := 0; i < stakingTxNumOutputs; i++ { + if i == stakingOutputIdx { + info, err := btcstaking.BuildStakingInfo( + sd.StakerKey, + []*btcec.PublicKey{sd.FinalityProviderKey}, + []*btcec.PublicKey{sd.CovenantKey}, + 1, + sd.StakingTime, + btcutil.Amount(r.Intn(5000)+minStakingValue), + &chaincfg.MainNetParams, + ) + + require.NoError(t, err) + stakingTx.AddTxOut(info.StakingOutput) + } else { + stakingTx.AddTxOut( + &wire.TxOut{ + PkScript: datagen.GenRandomByteArray(r, 32), + Value: int64(r.Intn(5000) + 1), + }, + ) + } + } + + // Always check case with min fee + testSlashingTx(r, t, stakingTx, stakingOutputIdx, slashingRate, int64(minFee), sd.StakerKey, slashingLockTime) + + // Check case with some random fee + fee := int64(r.Intn(1000) + minFee) + testSlashingTx(r, t, stakingTx, stakingOutputIdx, slashingRate, fee, sd.StakerKey, slashingLockTime) + }) +} + +func genRandomBTCAddress(r *rand.Rand) (*btcutil.AddressPubKeyHash, error) { + return btcutil.NewAddressPubKeyHash(datagen.GenRandomByteArray(r, 20), &chaincfg.MainNetParams) +} + +func taprootOutputWithValue(t *testing.T, r *rand.Rand, value btcutil.Amount) *wire.TxOut { + bytes := datagen.GenRandomByteArray(r, 32) + addrr, err := btcutil.NewAddressTaproot(bytes, &chaincfg.MainNetParams) + require.NoError(t, err) + return outputFromAddressAndValue(t, addrr, value) +} + +func outputFromAddressAndValue(t *testing.T, addr btcutil.Address, value btcutil.Amount) *wire.TxOut { + pkScript, err := txscript.PayToAddrScript(addr) + require.NoError(t, err) + return wire.NewTxOut(int64(value), pkScript) +} + +func testSlashingTx( + r *rand.Rand, + t *testing.T, + stakingTx *wire.MsgTx, + stakingOutputIdx int, + slashingRate sdkmath.LegacyDec, + fee int64, + stakerPk *btcec.PublicKey, + slashingChangeLockTime uint16, +) { + // Generate random slashing and change addresses + slashingAddress, err := genRandomBTCAddress(r) + require.NoError(t, err) + + // Construct slashing transaction using the provided parameters + slashingTx, err := btcstaking.BuildSlashingTxFromStakingTxStrict( + stakingTx, + uint32(stakingOutputIdx), + slashingAddress, + stakerPk, + slashingChangeLockTime, + fee, + slashingRate, + &chaincfg.MainNetParams, + ) + + if btcstaking.IsSlashingRateValid(slashingRate) { + // If the slashing rate is valid i.e., in the range (0,1) with at most 2 decimal places, + // it is still possible that the slashing transaction is invalid. The following checks will confirm that + // slashing tx is not constructed if + // - the change output has insufficient funds. + // - the change output is less than the dust threshold. + // - The slashing output is less than the dust threshold. + + slashingRateFloat64, err2 := slashingRate.Float64() + require.NoError(t, err2) + + stakingAmount := btcutil.Amount(stakingTx.TxOut[stakingOutputIdx].Value) + slashingAmount := stakingAmount.MulF64(slashingRateFloat64) + changeAmount := stakingAmount - slashingAmount - btcutil.Amount(fee) + + // check if the created outputs are not dust + slashingOutput := outputFromAddressAndValue(t, slashingAddress, slashingAmount) + changeOutput := taprootOutputWithValue(t, r, changeAmount) + + if changeAmount <= 0 { + require.Error(t, err) + require.ErrorIs(t, err, btcstaking.ErrInsufficientChangeAmount) + } else if mempool.IsDust(slashingOutput, mempool.DefaultMinRelayTxFee) || mempool.IsDust(changeOutput, mempool.DefaultMinRelayTxFee) { + require.Error(t, err) + require.ErrorIs(t, err, btcstaking.ErrDustOutputFound) + } else { + require.NoError(t, err) + err = btcstaking.CheckTransactions( + slashingTx, + stakingTx, + uint32(stakingOutputIdx), + fee, + slashingRate, + slashingAddress, + stakerPk, + slashingChangeLockTime, + &chaincfg.MainNetParams, + ) + require.NoError(t, err) + } + } else { + require.Error(t, err) + require.ErrorIs(t, err, btcstaking.ErrInvalidSlashingRate) + } +} + +func FuzzGeneratingSignatureValidation(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + pk, err := btcec.NewPrivateKey() + require.NoError(t, err) + inputHash, err := chainhash.NewHash(datagen.GenRandomByteArray(r, 32)) + require.NoError(t, err) + + tx := wire.NewMsgTx(2) + foundingOutput := wire.NewTxOut(int64(r.Intn(1000)), datagen.GenRandomByteArray(r, 32)) + tx.AddTxIn( + wire.NewTxIn(wire.NewOutPoint(inputHash, uint32(r.Intn(20))), nil, nil), + ) + tx.AddTxOut( + wire.NewTxOut(int64(r.Intn(1000)), datagen.GenRandomByteArray(r, 32)), + ) + script := datagen.GenRandomByteArray(r, 150) + + sig, err := btcstaking.SignTxWithOneScriptSpendInputFromScript( + tx, + foundingOutput, + pk, + script, + ) + + require.NoError(t, err) + + err = btcstaking.VerifyTransactionSigWithOutput( + tx, + foundingOutput, + script, + pk.PubKey(), + sig.Serialize(), + ) + + require.NoError(t, err) + }) +} diff --git a/btcstaking/types.go b/btcstaking/types.go new file mode 100644 index 000000000..9ef508069 --- /dev/null +++ b/btcstaking/types.go @@ -0,0 +1,557 @@ +package btcstaking + +import ( + "encoding/hex" + "fmt" + + sdkmath "cosmossdk.io/math" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +const ( + // Point with unknown discrete logarithm defined in: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs + // using it as internal public key efectively disables taproot key spends + unspendableKeyPath = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" +) + +var ( + unspendableKeyPathKey = unspendableKeyPathInternalPubKeyInternal(unspendableKeyPath) +) + +func unspendableKeyPathInternalPubKeyInternal(keyHex string) btcec.PublicKey { + keyBytes, err := hex.DecodeString(keyHex) + + if err != nil { + panic(fmt.Sprintf("unexpected error: %v", err)) + } + + // We are using btcec here, as key is 33 byte compressed format. + pubKey, err := btcec.ParsePubKey(keyBytes) + + if err != nil { + panic(fmt.Sprintf("unexpected error: %v", err)) + } + return *pubKey +} + +func unspendableKeyPathInternalPubKey() btcec.PublicKey { + return unspendableKeyPathKey +} + +func NewTaprootTreeFromScripts( + scripts [][]byte, +) *txscript.IndexedTapScriptTree { + var tapLeafs []txscript.TapLeaf + for _, script := range scripts { + scr := script + tapLeafs = append(tapLeafs, txscript.NewBaseTapLeaf(scr)) + } + return txscript.AssembleTaprootScriptTree(tapLeafs...) +} + +func DeriveTaprootAddress( + tapScriptTree *txscript.IndexedTapScriptTree, + internalPubKey *btcec.PublicKey, + net *chaincfg.Params) (*btcutil.AddressTaproot, error) { + + tapScriptRootHash := tapScriptTree.RootNode.TapHash() + + outputKey := txscript.ComputeTaprootOutputKey( + internalPubKey, tapScriptRootHash[:], + ) + + address, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(outputKey), net) + + if err != nil { + return nil, fmt.Errorf("error encoding Taproot address: %v", err) + } + + return address, nil +} + +func DeriveTaprootPkScript( + tapScriptTree *txscript.IndexedTapScriptTree, + internalPubKey *btcec.PublicKey, + net *chaincfg.Params, +) ([]byte, error) { + taprootAddress, err := DeriveTaprootAddress( + tapScriptTree, + &unspendableKeyPathKey, + net, + ) + + if err != nil { + return nil, err + } + + taprootPkScript, err := txscript.PayToAddrScript(taprootAddress) + + if err != nil { + return nil, err + } + + return taprootPkScript, nil +} + +type taprootScriptHolder struct { + internalPubKey *btcec.PublicKey + scriptTree *txscript.IndexedTapScriptTree +} + +func newTaprootScriptHolder( + internalPubKey *btcec.PublicKey, + scripts [][]byte, +) (*taprootScriptHolder, error) { + if internalPubKey == nil { + return nil, fmt.Errorf("internal public key is nil") + } + + if len(scripts) == 0 { + return &taprootScriptHolder{ + scriptTree: txscript.NewIndexedTapScriptTree(0), + }, nil + } + + createdLeafs := make(map[chainhash.Hash]bool) + tapLeafs := make([]txscript.TapLeaf, len(scripts)) + + for i, s := range scripts { + script := s + if len(script) == 0 { + return nil, fmt.Errorf("cannot build tree with empty script") + } + + tapLeaf := txscript.NewBaseTapLeaf(script) + leafHash := tapLeaf.TapHash() + + if _, ok := createdLeafs[leafHash]; ok { + return nil, fmt.Errorf("duplicate script in provided scripts") + } + + createdLeafs[leafHash] = true + tapLeafs[i] = tapLeaf + } + + scriptTree := txscript.AssembleTaprootScriptTree(tapLeafs...) + + return &taprootScriptHolder{ + internalPubKey: internalPubKey, + scriptTree: scriptTree, + }, nil +} + +func (t *taprootScriptHolder) scriptSpendInfoByName( + leafHash chainhash.Hash, +) (*SpendInfo, error) { + scriptIdx, ok := t.scriptTree.LeafProofIndex[leafHash] + + if !ok { + return nil, fmt.Errorf("script not found in script tree") + } + + merkleProof := t.scriptTree.LeafMerkleProofs[scriptIdx] + + return &SpendInfo{ + ControlBlock: merkleProof.ToControlBlock(t.internalPubKey), + RevealedLeaf: merkleProof.TapLeaf, + }, nil +} + +func (t *taprootScriptHolder) taprootPkScript(net *chaincfg.Params) ([]byte, error) { + return DeriveTaprootPkScript( + t.scriptTree, + t.internalPubKey, + net, + ) +} + +// Package responsible for different kinds of btc scripts used by babylon +// Staking script has 3 spending paths: +// 1. Staker can spend after relative time lock - staking +// 2. Staker can spend with covenat cooperation any time +// 3. Staker can spend with finality provider and covenant cooperation any time. +type StakingInfo struct { + StakingOutput *wire.TxOut + scriptHolder *taprootScriptHolder + timeLockPathLeafHash chainhash.Hash + unbondingPathLeafHash chainhash.Hash + slashingPathLeafHash chainhash.Hash +} + +// GetPkScript returns the full staking taproot pkscript in the corresponding staking tx +func (sti *StakingInfo) GetPkScript() []byte { + return sti.StakingOutput.PkScript +} + +// GetOutputFetcher returns the fetcher of the staking tx's output +func (sti *StakingInfo) GetOutputFetcher() *txscript.CannedPrevOutputFetcher { + return txscript.NewCannedPrevOutputFetcher( + sti.GetPkScript(), sti.StakingOutput.Value, + ) +} + +// SpendInfo contains information necessary to create witness for given script +type SpendInfo struct { + // Control block contains merkle proof of inclusion of revealed script path + ControlBlock txscript.ControlBlock + // RevealedLeaf is the leaf of the script tree which is revealed i.e scriptpath + // which is being executed + RevealedLeaf txscript.TapLeaf +} + +// GetPkScriptPath returns the path of the taproot pkscript corresponding +// to the triggered spending condition of the tx associated with the SpendInfo +func (si *SpendInfo) GetPkScriptPath() []byte { + return si.RevealedLeaf.Script +} + +func SpendInfoFromRevealedScript( + revealedScript []byte, + internalKey *btcec.PublicKey, + tree *txscript.IndexedTapScriptTree) (*SpendInfo, error) { + + revealedLeaf := txscript.NewBaseTapLeaf(revealedScript) + leafHash := revealedLeaf.TapHash() + + scriptIdx, ok := tree.LeafProofIndex[leafHash] + + if !ok { + return nil, fmt.Errorf("script not found in script tree") + } + + merkleProof := tree.LeafMerkleProofs[scriptIdx] + + return &SpendInfo{ + ControlBlock: merkleProof.ToControlBlock(internalKey), + RevealedLeaf: revealedLeaf, + }, nil +} + +func aggregateScripts(scripts ...[]byte) []byte { + if len(scripts) == 0 { + return []byte{} + } + + var finalScript []byte + + for _, script := range scripts { + finalScript = append(finalScript, script...) + } + return finalScript +} + +// babylonScriptPaths contains all possible babylon script paths +// not every babylon output will contain all of those paths +type babylonScriptPaths struct { + // timeLockPathScript is the script path for normal unbonding + // OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY + timeLockPathScript []byte + // unbondingPathScript is the script path for on-demand early unbonding + // OP_CHECKSIGVERIFY + // OP_CHECKSIG ... OP_CHECKSIGADD M OP_GREATERTHANOREQUAL OP_VERIFY + unbondingPathScript []byte + // slashingPathScript is the script path for slashing + // OP_CHECKSIGVERIFY + // OP_CHECKSIG ... OP_CHECKSIGADD M OP_GREATERTHANOREQUAL OP_VERIFY + // OP_CHECKSIG ... OP_CHECKSIGADD 1 OP_GREATERTHANOREQUAL OP_VERIFY + slashingPathScript []byte +} + +func newBabylonScriptPaths( + stakerKey *btcec.PublicKey, + fpKeys []*btcec.PublicKey, + covenantKeys []*btcec.PublicKey, + covenantQuorum uint32, + lockTime uint16, +) (*babylonScriptPaths, error) { + if stakerKey == nil { + return nil, fmt.Errorf("staker key is nil") + } + + timeLockPathScript, err := buildTimeLockScript(stakerKey, lockTime) + + if err != nil { + return nil, err + } + + covenantMultisigScript, err := buildMultiSigScript( + covenantKeys, + covenantQuorum, + // covenant multisig is always last in script so we do not run verify and leave + // last value on the stack. If we do not leave at least one element on the stack + // script will always error + false, + ) + + if err != nil { + return nil, err + } + + stakerSigScript, err := buildSingleKeySigScript(stakerKey, true) + + if err != nil { + return nil, err + } + + fpSigScript, err := buildMultiSigScript( + fpKeys, + // we always require only one finality provider to sign + 1, + // we need to run verify to clear the stack, as finality provider multisig is in the middle of the script + true, + ) + + if err != nil { + return nil, err + } + + unbondingPathScript := aggregateScripts( + stakerSigScript, + covenantMultisigScript, + ) + + slashingPathScript := aggregateScripts( + stakerSigScript, + fpSigScript, + covenantMultisigScript, + ) + + return &babylonScriptPaths{ + timeLockPathScript: timeLockPathScript, + unbondingPathScript: unbondingPathScript, + slashingPathScript: slashingPathScript, + }, nil +} + +func BuildStakingInfo( + stakerKey *btcec.PublicKey, + fpKeys []*btcec.PublicKey, + covenantKeys []*btcec.PublicKey, + covenantQuorum uint32, + stakingTime uint16, + stakingAmount btcutil.Amount, + net *chaincfg.Params, +) (*StakingInfo, error) { + unspendableKeyPathKey := unspendableKeyPathInternalPubKey() + + babylonScripts, err := newBabylonScriptPaths( + stakerKey, + fpKeys, + covenantKeys, + covenantQuorum, + stakingTime, + ) + + if err != nil { + return nil, err + } + + var unbondingPaths [][]byte + unbondingPaths = append(unbondingPaths, babylonScripts.timeLockPathScript) + unbondingPaths = append(unbondingPaths, babylonScripts.unbondingPathScript) + unbondingPaths = append(unbondingPaths, babylonScripts.slashingPathScript) + + timeLockLeafHash := txscript.NewBaseTapLeaf(babylonScripts.timeLockPathScript).TapHash() + unbondingPathLeafHash := txscript.NewBaseTapLeaf(babylonScripts.unbondingPathScript).TapHash() + slashingLeafHash := txscript.NewBaseTapLeaf(babylonScripts.slashingPathScript).TapHash() + + sh, err := newTaprootScriptHolder( + &unspendableKeyPathKey, + unbondingPaths, + ) + + if err != nil { + return nil, err + } + + taprootPkScript, err := sh.taprootPkScript(net) + + if err != nil { + return nil, err + } + + stakingOutput := wire.NewTxOut(int64(stakingAmount), taprootPkScript) + + return &StakingInfo{ + StakingOutput: stakingOutput, + scriptHolder: sh, + timeLockPathLeafHash: timeLockLeafHash, + unbondingPathLeafHash: unbondingPathLeafHash, + slashingPathLeafHash: slashingLeafHash, + }, nil +} + +func (i *StakingInfo) TimeLockPathSpendInfo() (*SpendInfo, error) { + return i.scriptHolder.scriptSpendInfoByName(i.timeLockPathLeafHash) +} + +func (i *StakingInfo) UnbondingPathSpendInfo() (*SpendInfo, error) { + return i.scriptHolder.scriptSpendInfoByName(i.unbondingPathLeafHash) +} + +func (i *StakingInfo) SlashingPathSpendInfo() (*SpendInfo, error) { + return i.scriptHolder.scriptSpendInfoByName(i.slashingPathLeafHash) +} + +// Unbonding script has 2 spending paths: +// 1. Staker can spend after relative time lock - staking +// 2. Staker can spend with finality provider and covenant cooperation any time. +type UnbondingInfo struct { + UnbondingOutput *wire.TxOut + scriptHolder *taprootScriptHolder + timeLockPathLeafHash chainhash.Hash + slashingPathLeafHash chainhash.Hash +} + +func BuildUnbondingInfo( + stakerKey *btcec.PublicKey, + fpKeys []*btcec.PublicKey, + covenantKeys []*btcec.PublicKey, + covenantQuorum uint32, + unbondingTime uint16, + unbondingAmount btcutil.Amount, + net *chaincfg.Params, +) (*UnbondingInfo, error) { + unspendableKeyPathKey := unspendableKeyPathInternalPubKey() + + babylonScripts, err := newBabylonScriptPaths( + stakerKey, + fpKeys, + covenantKeys, + covenantQuorum, + unbondingTime, + ) + + if err != nil { + return nil, err + } + + var unbondingPaths [][]byte + unbondingPaths = append(unbondingPaths, babylonScripts.timeLockPathScript) + unbondingPaths = append(unbondingPaths, babylonScripts.slashingPathScript) + + timeLockLeafHash := txscript.NewBaseTapLeaf(babylonScripts.timeLockPathScript).TapHash() + slashingLeafHash := txscript.NewBaseTapLeaf(babylonScripts.slashingPathScript).TapHash() + + sh, err := newTaprootScriptHolder( + &unspendableKeyPathKey, + unbondingPaths, + ) + + if err != nil { + return nil, err + } + + taprootPkScript, err := sh.taprootPkScript(net) + + if err != nil { + return nil, err + } + + unbondingOutput := wire.NewTxOut(int64(unbondingAmount), taprootPkScript) + + return &UnbondingInfo{ + UnbondingOutput: unbondingOutput, + scriptHolder: sh, + timeLockPathLeafHash: timeLockLeafHash, + slashingPathLeafHash: slashingLeafHash, + }, nil +} + +func (i *UnbondingInfo) TimeLockPathSpendInfo() (*SpendInfo, error) { + return i.scriptHolder.scriptSpendInfoByName(i.timeLockPathLeafHash) +} + +func (i *UnbondingInfo) SlashingPathSpendInfo() (*SpendInfo, error) { + return i.scriptHolder.scriptSpendInfoByName(i.slashingPathLeafHash) +} + +// IsSlashingRateValid checks if the given slashing rate is between the valid range i.e., (0,1) with a precision of at most 2 decimal places. +func IsSlashingRateValid(slashingRate sdkmath.LegacyDec) bool { + // Check if the slashing rate is between 0 and 1 + if slashingRate.LTE(sdkmath.LegacyZeroDec()) || slashingRate.GTE(sdkmath.LegacyOneDec()) { + return false + } + + // Multiply by 100 to move the decimal places and check if precision is at most 2 decimal places + multipliedRate := slashingRate.Mul(sdkmath.LegacyNewDec(100)) + + // Truncate the rate to remove decimal places + truncatedRate := multipliedRate.TruncateDec() + + // Check if the truncated rate is equal to the original rate + return multipliedRate.Equal(truncatedRate) +} + +type RelativeTimeLockTapScriptInfo struct { + // data necessary to build witness for given script + SpendInfo *SpendInfo + // lock time in script, required to set proper sequence number when spending output + // with relative time lock + LockTime uint16 + // taproot address of the script + TapAddress btcutil.Address + // pkscript in output wchich commits to the given script/leaf + PkScript []byte +} + +func BuildRelativeTimelockTaprootScript( + pk *btcec.PublicKey, + lockTime uint16, + net *chaincfg.Params, +) (*RelativeTimeLockTapScriptInfo, error) { + unspendableKeyPathKey := unspendableKeyPathInternalPubKey() + + script, err := buildTimeLockScript(pk, lockTime) + + if err != nil { + return nil, err + } + + sh, err := newTaprootScriptHolder( + &unspendableKeyPathKey, + [][]byte{script}, + ) + + if err != nil { + return nil, err + } + + // there is only one script path in tree, so we can use index 0 + proof := sh.scriptTree.LeafMerkleProofs[0] + + spendInfo := &SpendInfo{ + ControlBlock: proof.ToControlBlock(&unspendableKeyPathKey), + RevealedLeaf: proof.TapLeaf, + } + + taprootAddress, err := DeriveTaprootAddress( + sh.scriptTree, + &unspendableKeyPathKey, + net, + ) + + if err != nil { + return nil, err + } + + taprootPkScript, err := txscript.PayToAddrScript(taprootAddress) + + if err != nil { + return nil, err + } + + return &RelativeTimeLockTapScriptInfo{ + SpendInfo: spendInfo, + LockTime: lockTime, + TapAddress: taprootAddress, + PkScript: taprootPkScript, + }, nil +} diff --git a/btcstaking/witness_utils.go b/btcstaking/witness_utils.go new file mode 100644 index 000000000..a733ab1e9 --- /dev/null +++ b/btcstaking/witness_utils.go @@ -0,0 +1,120 @@ +package btcstaking + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/wire" +) + +func (si *SpendInfo) CreateTimeLockPathWitness(delegatorSig *schnorr.Signature) (wire.TxWitness, error) { + if si == nil { + panic("cannot build witness without spend info") + } + if delegatorSig == nil { + return nil, fmt.Errorf("delegator signature should not be nil") + } + return CreateWitness(si, [][]byte{delegatorSig.Serialize()}) +} + +func (si *SpendInfo) CreateUnbondingPathWitness(covenantSigs []*schnorr.Signature, delegatorSig *schnorr.Signature) (wire.TxWitness, error) { + if si == nil { + panic("cannot build witness without spend info") + } + + var witnessStack [][]byte + + // add covenant signatures to witness stack + // NOTE: only a quorum number of covenant signatures needs to be non-nil + if len(covenantSigs) == 0 { + return nil, fmt.Errorf("covenant signatures should not be empty") + } + for _, covSig := range covenantSigs { + if covSig == nil { + witnessStack = append(witnessStack, []byte{}) + } else { + witnessStack = append(witnessStack, covSig.Serialize()) + } + } + + // add delegator signature to witness stack + if delegatorSig == nil { + return nil, fmt.Errorf("delegator signature should not be nil") + } + witnessStack = append(witnessStack, delegatorSig.Serialize()) + + return CreateWitness(si, witnessStack) +} + +func (si *SpendInfo) CreateSlashingPathWitness(covenantSigs []*schnorr.Signature, fpSigs []*schnorr.Signature, delegatorSig *schnorr.Signature) (wire.TxWitness, error) { + if si == nil { + panic("cannot build witness without spend info") + } + + var witnessStack [][]byte + + // add covenant signatures to witness stack + // NOTE: only a quorum number of covenant signatures needs to be non-nil + if len(covenantSigs) == 0 { + return nil, fmt.Errorf("covenant signatures should not be empty") + } + for _, covSig := range covenantSigs { + if covSig == nil { + witnessStack = append(witnessStack, []byte{}) + } else { + witnessStack = append(witnessStack, covSig.Serialize()) + } + } + + // add finality provider signatures to witness stack + // NOTE: only 1 of the finality provider signatures needs to be non-nil + if len(fpSigs) == 0 { + return nil, fmt.Errorf("finality provider signatures should not be empty") + } + for _, fpSig := range fpSigs { + if fpSig == nil { + witnessStack = append(witnessStack, []byte{}) + } else { + witnessStack = append(witnessStack, fpSig.Serialize()) + } + } + + // add delegator signature to witness stack + if delegatorSig == nil { + return nil, fmt.Errorf("delegator signature should not be nil") + } + witnessStack = append(witnessStack, delegatorSig.Serialize()) + + return CreateWitness(si, witnessStack) +} + +// createWitness creates witness for spending the tx corresponding to +// the given spend info with the given stack of signatures +// The returned witness stack follows the structure below: +// - first come signatures +// - then whole revealed script +// - then control block +func CreateWitness(si *SpendInfo, signatures [][]byte) (wire.TxWitness, error) { + numSignatures := len(signatures) + + controlBlockBytes, err := si.ControlBlock.ToBytes() + if err != nil { + return nil, err + } + + // witness stack has: + // all signatures + // whole revealed script + // control block + witnessStack := wire.TxWitness(make([][]byte, numSignatures+2)) + + for i, sig := range signatures { + sc := sig + witnessStack[i] = sc + } + + witnessStack[numSignatures] = si.GetPkScriptPath() + witnessStack[numSignatures+1] = controlBlockBytes + + return witnessStack, nil +} diff --git a/btctxformatter/formatter.go b/btctxformatter/formatter.go index 527e37a4a..14dde7b8b 100644 --- a/btctxformatter/formatter.go +++ b/btctxformatter/formatter.go @@ -25,7 +25,7 @@ type BabylonData struct { type RawBtcCheckpoint struct { Epoch uint64 - LastCommitHash []byte + BlockHash []byte BitMap []byte SubmitterAddress []byte BlsSig []byte @@ -43,8 +43,11 @@ const ( // 4bytes tag + 4 bits version + 4 bits part index headerLength = TagLength + 1 - LastCommitHashLength = 32 + BlockHashLength = 32 + // BitMapLength is the number of bytes in a bitmap + // It is the minimal number needed for supporting 100 + // validators in BTC timestamping, since 13*8 = 104 BitMapLength = 13 AddressLength = 20 @@ -61,11 +64,11 @@ const ( // 8 bytes are for 64bit unsigned epoch number EpochLength = 8 - firstPartLength = headerLength + LastCommitHashLength + AddressLength + EpochLength + BitMapLength + firstPartLength = headerLength + BlockHashLength + AddressLength + EpochLength + BitMapLength secondPartLength = headerLength + BlsSigLength + firstPartHashLength - RawBTCCheckpointLength = EpochLength + LastCommitHashLength + BitMapLength + BlsSigLength + AddressLength + RawBTCCheckpointLength = EpochLength + BlockHashLength + BitMapLength + BlsSigLength + AddressLength ) func getVerHalf(version FormatVersion, halfNumber uint8) uint8 { @@ -96,7 +99,7 @@ func encodeFirstOpRetrun( tag BabylonTag, version FormatVersion, epoch uint64, - lastCommitHash []byte, + appHash []byte, bitMap []byte, submitterAddress []byte, ) []byte { @@ -107,7 +110,7 @@ func encodeFirstOpRetrun( serializedBytes = append(serializedBytes, U64ToBEBytes(epoch)...) - serializedBytes = append(serializedBytes, lastCommitHash...) + serializedBytes = append(serializedBytes, appHash...) serializedBytes = append(serializedBytes, bitMap...) @@ -154,8 +157,8 @@ func EncodeCheckpointData( return nil, nil, errors.New("invalid format version") } - if len(rawBTCCheckpoint.LastCommitHash) != LastCommitHashLength { - return nil, nil, errors.New("lastCommitHash should have 32 bytes") + if len(rawBTCCheckpoint.BlockHash) != BlockHashLength { + return nil, nil, errors.New("appHash should have 32 bytes") } if len(rawBTCCheckpoint.BitMap) != BitMapLength { @@ -174,7 +177,7 @@ func EncodeCheckpointData( tag, version, rawBTCCheckpoint.Epoch, - rawBTCCheckpoint.LastCommitHash, + rawBTCCheckpoint.BlockHash, rawBTCCheckpoint.BitMap, rawBTCCheckpoint.SubmitterAddress, ) @@ -211,7 +214,7 @@ func parseHeader( header := formatHeader{ tag: BabylonTag(tagBytes), - version: FormatVersion((verHalf & 0xf)), + version: FormatVersion(verHalf & 0xf), part: verHalf >> 4, } @@ -220,7 +223,7 @@ func parseHeader( func (header *formatHeader) validateHeader( expectedTag BabylonTag, - supportedVersion FormatVersion, + _ FormatVersion, expectedPart uint8, ) error { if !bytes.Equal(header.tag, expectedTag) { @@ -304,7 +307,7 @@ func IsBabylonCheckpointData( return nil, errors.New("not valid babylon data") } -// DecodeRawCheckpoint extracts epoch, lastCommitHash, bitmap, and blsSig from a +// DecodeRawCheckpoint extracts epoch, appHash, bitmap, and blsSig from a // flat byte array and compose them into a RawCheckpoint struct func DecodeRawCheckpoint(version FormatVersion, btcCkptBytes []byte) (*RawBtcCheckpoint, error) { if version > CurrentVersion { @@ -318,14 +321,14 @@ func DecodeRawCheckpoint(version FormatVersion, btcCkptBytes []byte) (*RawBtcChe var b bytes.Buffer b.Write(btcCkptBytes) epochBytes := b.Next(EpochLength) - lchBytes := b.Next(LastCommitHashLength) + appHashBytes := b.Next(BlockHashLength) bitmapBytes := b.Next(BitMapLength) addressBytes := b.Next(AddressLength) blsSigBytes := b.Next(BlsSigLength) rawCheckpoint := &RawBtcCheckpoint{ Epoch: binary.BigEndian.Uint64(epochBytes), - LastCommitHash: lchBytes, + BlockHash: appHashBytes, BitMap: bitmapBytes, SubmitterAddress: addressBytes, BlsSig: blsSigBytes, diff --git a/btctxformatter/formatter_test.go b/btctxformatter/formatter_test.go index 06b9ea10f..1215a81cf 100644 --- a/btctxformatter/formatter_test.go +++ b/btctxformatter/formatter_test.go @@ -8,17 +8,17 @@ import ( ) func randNBytes(n int) []byte { - bytes := make([]byte, n) - cprand.Read(bytes) //nolint:errcheck // This is a test. - return bytes + bz := make([]byte, n) + cprand.Read(bz) //nolint:errcheck // This is a test. + return bz } func FuzzEncodingDecoding(f *testing.F) { - f.Add(uint64(5), randNBytes(TagLength), randNBytes(LastCommitHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) - f.Add(uint64(20), randNBytes(TagLength), randNBytes(LastCommitHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) - f.Add(uint64(2000), randNBytes(TagLength), randNBytes(LastCommitHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) + f.Add(uint64(5), randNBytes(TagLength), randNBytes(BlockHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) + f.Add(uint64(20), randNBytes(TagLength), randNBytes(BlockHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) + f.Add(uint64(2000), randNBytes(TagLength), randNBytes(BlockHashLength), randNBytes(BitMapLength), randNBytes(BlsSigLength), randNBytes(AddressLength)) - f.Fuzz(func(t *testing.T, epoch uint64, tag []byte, lastCommitHash []byte, bitMap []byte, blsSig []byte, address []byte) { + f.Fuzz(func(t *testing.T, epoch uint64, tag []byte, appHash []byte, bitMap []byte, blsSig []byte, address []byte) { if len(tag) < TagLength { t.Skip("Tag should have 4 bytes") @@ -28,7 +28,7 @@ func FuzzEncodingDecoding(f *testing.F) { rawBTCCkpt := &RawBtcCheckpoint{ Epoch: epoch, - LastCommitHash: lastCommitHash, + BlockHash: appHash, BitMap: bitMap, SubmitterAddress: blsSig, BlsSig: address, @@ -78,8 +78,8 @@ func FuzzEncodingDecoding(f *testing.F) { t.Errorf("Epoch should match. Expected: %v. Got: %v", epoch, ckpt.Epoch) } - if !bytes.Equal(lastCommitHash, ckpt.LastCommitHash) { - t.Errorf("LastCommitHash should match. Expected: %v. Got: %v", lastCommitHash, ckpt.LastCommitHash) + if !bytes.Equal(appHash, ckpt.BlockHash) { + t.Errorf("BlockHash should match. Expected: %v. Got: %v", appHash, ckpt.BlockHash) } if !bytes.Equal(bitMap, ckpt.BitMap) { @@ -99,7 +99,7 @@ func FuzzDecodingWontPanic(f *testing.F) { f.Fuzz(func(t *testing.T, bytes []byte, tagIdx uint8) { tag := []byte{0, 1, 2, 3} - decoded, err := IsBabylonCheckpointData(BabylonTag(tag), CurrentVersion, bytes) + decoded, err := IsBabylonCheckpointData(tag, CurrentVersion, bytes) if err == nil { if decoded.Index != 0 && decoded.Index != 1 { diff --git a/client/docs/diagrams/create_raw_checkpoint.puml b/client/docs/diagrams/create_raw_checkpoint.puml index 17dfff17b..de1990280 100644 --- a/client/docs/diagrams/create_raw_checkpoint.puml +++ b/client/docs/diagrams/create_raw_checkpoint.puml @@ -36,21 +36,21 @@ end note == Block N+2: The block that contains the Quorum Certificate of the old validators over Block N+1 == Tendermint -> checkpointing : BeginBlock -checkpointing -> rawckpts : Store header.LastCommitHash +checkpointing -> rawckpts : Store header.AppHash note left LastCommitInfo in BeginBlock does not contain signatures, - but we are signing the LastCommitHash, the Merkle root of + but we are signing the AppHash, the Merkle root of signatures, and that is available in the Header. end note loop on each old validator node - Tendermint --> signer ++ : Observe block.header.LastCommitHash + Tendermint --> signer ++ : Observe block.header.AppHash note right The full block has the signatures, i.e. the Quorum Certificate (Q.C.), but all we need is the hash. end note - signer -> signer : Sign LastCommitHash + signer -> signer : Sign AppHash signer -> Tendermint -- : AddBlsSig Tendermint -> checkpointing ++: CheckTx note right @@ -73,7 +73,7 @@ loop for each AddBlsSig tx checkpointing -> checkpointing : Check BLS signature over full Q.C. checkpointing -> staking : Get validator power at \n beginning of previous epoch checkpointing -> checkpointing : Check eligibility to sign BLS - checkpointing -> rawckpts : Get LastCommitHash for the uncheckpointed epoch + checkpointing -> rawckpts : Get AppHash for the uncheckpointed epoch checkpointing -> checkpointing : Check that Q.C. hash in ledger matches the tx alt valid transaction diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 14f146ca2..cccf807c2 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -87,8 +87,7 @@ paths: valideated the proof? title: >- - TransactionInfo is the info of a tx that contains - Babylon checkpoint, + TransactionInfo is the info of a tx on Bitcoin, including @@ -404,16 +403,11 @@ paths: already processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains - Babylon checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: the BTC checkpoint transactions of the best submission best_submission_vigilante_address_list: @@ -826,6 +820,57 @@ paths: format: byte tags: - Query + /babylon/btclightclient/v1/depth/{hash}: + get: + summary: >- + HeaderDepth returns the depth of the header in main chain or error if + the + + block is not found or it exists on fork + operationId: HeaderDepth + responses: + '200': + description: A successful response. + schema: + type: object + properties: + depth: + type: string + format: uint64 + title: >- + QueryMainChainDepthResponse is the response type for the + Query/MainChainDepth RPC + + it contains depth of the block in main chain + 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 + value: + type: string + format: byte + parameters: + - name: hash + in: path + required: true + type: string + tags: + - Query /babylon/btclightclient/v1/hashes: get: summary: Hashes retrieves the hashes maintained by the module. @@ -1103,6 +1148,56 @@ paths: type: boolean tags: - Query + /babylon/btclightclient/v1/params: + get: + summary: Params queries the parameters of the module. + operationId: BtcLightClientParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + insert_headers_allow_list: + type: array + items: + type: string + title: >- + List of addresses which are allowed to insert headers to + btc light client + + if the list is empty, any address can insert headers + 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 + value: + type: string + format: byte + tags: + - Query /babylon/btclightclient/v1/tip: get: summary: Tip return best header on canonical chain @@ -1302,6 +1397,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -1341,7 +1440,6 @@ paths: name "y.z". - JSON @@ -1423,6 +1521,22 @@ paths: delegation val_addr: type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements + the custom method + + signatures required by gogoproto. block_height: type: string format: uint64 @@ -1555,6 +1669,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -1594,7 +1712,6 @@ paths: name "y.z". - JSON @@ -1664,102 +1781,36 @@ paths: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time + of this epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block in + this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in - this epoch. + last_block_time is the time of the last block in this + epoch. - Babylon needs to remember the last header of each epoch - to complete + Babylon needs to remember the last header's time of each + epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte @@ -1768,90 +1819,26 @@ paths: epoch It will be used for proving a block is in an epoch - sealer_header: + sealer_app_hash: + type: string + format: byte title: >- - sealer_header is the 2nd header of the next epoch + sealer is the last block of the sealed epoch - This validator set has generated a BLS multisig on - `last_commit_hash` of + sealer_app_hash points to the sealer but stored in the + 1st header - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, + of the next epoch + sealer_block_hash: + type: string + format: byte + title: >- + sealer_block_hash is the hash of the sealer - including all blockchain data structures and the - rules of the application's + the validator set has generated a BLS multisig on the + hash, - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + i.e., hash of the last block in the epoch title: Epoch is a structure that contains the metadata of an epoch pagination: title: pagination defines the pagination in the response @@ -1998,6 +1985,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -2037,7 +2028,6 @@ paths: name "y.z". - JSON @@ -2151,194 +2141,64 @@ paths: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time + of this epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block in + this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to - complete + Babylon needs to remember the last header's time of each + epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the - epoch's beginning, and + finalised. The last_block_time field is nil in the epoch's + beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, + app_hash_root: + type: string + format: byte + title: >- + app_hash_root is the Merkle root of all AppHashs in this + epoch - including all blockchain data structures and the rules - of the application's + It will be used for proving a block is in an epoch + sealer_app_hash: + type: string + format: byte + title: >- + sealer is the last block of the sealed epoch - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - app_hash_root: + sealer_app_hash points to the sealer but stored in the 1st + header + + of the next epoch + sealer_block_hash: type: string format: byte title: >- - app_hash_root is the Merkle root of all AppHashs in this - epoch - - It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, + sealer_block_hash is the hash of the sealer - including all blockchain data structures and the rules - of the application's + the validator set has generated a BLS multisig on the + hash, - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + i.e., hash of the last block in the epoch title: Epoch is a structure that contains the metadata of an epoch title: >- QueryEpochInfoRequest is the response type for the Query/EpochInfo @@ -2456,6 +2316,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -2495,7 +2359,6 @@ paths: name "y.z". - JSON @@ -2635,6 +2498,15 @@ paths: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in + MsgCreateValidator is deprecated. + + The validator address bytes and delegator address + bytes refer to the same account while creating + validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -2737,6 +2609,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -2776,7 +2652,6 @@ paths: name "y.z". - JSON @@ -2923,11 +2798,47 @@ paths: of coins from a delegator and source validator to a destination validator. + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements + the custom method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding + delegation entry balance + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for + performing a cancel unbonding delegation for delegator title: >- QueuedMessage is a message that can change the validator set and is delayed - to the epoch boundary + to the end of an epoch title: msgs is the list of messages queued in the current epoch pagination: title: pagination defines the pagination in the response @@ -3076,6 +2987,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -3115,7 +3030,6 @@ paths: name "y.z". - JSON @@ -3392,6 +3306,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -3431,7 +3349,6 @@ paths: name "y.z". - JSON @@ -3642,6 +3559,15 @@ paths: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in + MsgCreateValidator is deprecated. + + The validator address bytes and delegator + address bytes refer to the same account while + creating validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -3747,6 +3673,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -3787,7 +3717,6 @@ paths: name "y.z". - JSON @@ -3934,11 +3863,48 @@ paths: of coins from a delegator and source validator to a destination validator. + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and + an amount. + + + NOTE: The amount field is an Int which + implements the custom method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to + unbonding delegation entry balance + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the + unbonding took place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK + message for performing a cancel unbonding + delegation for delegator title: >- QueuedMessage is a message that can change the validator set and is delayed - to the epoch boundary + to the end of an epoch title: >- QueuedMessageList is a message that contains a list of staking-related @@ -4093,6 +4059,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -4132,7 +4102,6 @@ paths: name "y.z". - JSON @@ -4378,6 +4347,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -4417,7 +4390,6 @@ paths: name "y.z". - JSON @@ -4627,6 +4599,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -4666,7 +4642,6 @@ paths: name "y.z". - JSON @@ -4733,12 +4708,17 @@ paths: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the + given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -4999,14 +4979,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the hash + of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -5021,7 +5001,7 @@ paths: from individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata description: |- QueryLastCheckpointWithStatusResponse is the response type for the Query/LastCheckpointWithStatus RPC method. @@ -5082,14 +5062,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the + hash of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -5104,7 +5084,7 @@ paths: from individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -5185,7 +5165,7 @@ paths: height) of this transition. - description: RawCheckpointWithMeta wraps the raw checkpoint with meta data. + description: RawCheckpointWithMeta wraps the raw checkpoint with metadata. description: >- QueryRawCheckpointResponse is the response type for the Query/RawCheckpoint @@ -5248,14 +5228,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the + hash of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -5270,7 +5250,7 @@ paths: aggregated from individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -5352,8 +5332,8 @@ paths: transition. description: >- - RawCheckpointWithMeta wraps the raw checkpoint with meta - data. + RawCheckpointWithMeta wraps the raw checkpoint with + metadata. title: >- the order is going from the newest to oldest based on the epoch number @@ -5486,14 +5466,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the + hash of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -5508,7 +5488,7 @@ paths: aggregated from individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -5590,8 +5570,8 @@ paths: transition. description: >- - RawCheckpointWithMeta wraps the raw checkpoint with meta - data. + RawCheckpointWithMeta wraps the raw checkpoint with + metadata. title: >- the order is going from the newest to oldest based on the epoch number @@ -5747,88 +5727,22 @@ paths: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, - - including all blockchain data structures and the rules - of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -5879,88 +5793,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -6129,6 +5977,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -6168,7 +6020,6 @@ paths: name "y.z". - JSON @@ -6381,6 +6232,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -6420,7 +6275,6 @@ paths: name "y.z". - JSON @@ -6567,88 +6421,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: - title: >- - babylon_header is the header of the babylon block + babylon_header_hash: + type: string + format: byte + title: >- + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -6703,92 +6491,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures - and the rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the + babylon block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: >- - hashes from the app output from the prev - block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: >- - Header defines the structure of a block - header. + header babylon_epoch: type: string format: uint64 @@ -6971,6 +6689,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -7010,7 +6732,6 @@ paths: name "y.z". - JSON @@ -7108,88 +6829,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -7244,92 +6899,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures - and the rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the + babylon block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: >- - hashes from the app output from the prev - block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: >- - Header defines the structure of a block - header. + header babylon_epoch: type: string format: uint64 @@ -7515,6 +7100,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -7554,7 +7143,6 @@ paths: name "y.z". - JSON @@ -7657,88 +7245,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -7792,90 +7314,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and - the rules of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: >- - hashes from the app output from the prev - block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the + babylon block that includes this CZ + + header babylon_epoch: type: string format: uint64 @@ -7949,102 +7403,36 @@ paths: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time + of this epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block in + this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to - complete + Babylon needs to remember the last header's time of each + epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the - epoch's beginning, and + finalised. The last_block_time field is nil in the epoch's + beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, - - including all blockchain data structures and the rules - of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte @@ -8053,90 +7441,26 @@ paths: epoch It will be used for proving a block is in an epoch - sealer_header: + sealer_app_hash: + type: string + format: byte title: >- - sealer_header is the 2nd header of the next epoch + sealer is the last block of the sealed epoch - This validator set has generated a BLS multisig on - `last_commit_hash` of + sealer_app_hash points to the sealer but stored in the 1st + header - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing - a block in the blockchain, + of the next epoch + sealer_block_hash: + type: string + format: byte + title: >- + sealer_block_hash is the hash of the sealer - including all blockchain data structures and the rules - of the application's + the validator set has generated a BLS multisig on the + hash, - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + i.e., hash of the last block in the epoch raw_checkpoint: title: raw_checkpoint is the raw checkpoint of this epoch type: object @@ -8147,14 +7471,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the hash + of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -8198,62 +7522,35 @@ paths: title: proof is the proof that the chain info is finalized type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries the - header is included + proof_cz_header_in_epoch is the proof that the CZ header + is timestamped - in a certain Babylon block - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a - transaction in the Merkle tree. - proof_header_in_epoch: + within a certain epoch type: object properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon header - is in a certain + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: >- + ProofOp defines an operation used for calculating + Merkle root - epoch + The data could be arbitrary format, providing + nessecary data + + for example neighbouring node hash proof_epoch_sealed: title: proof_epoch_sealed is the proof that the epoch is sealed type: object @@ -8265,12 +7562,21 @@ paths: properties: validator_address: type: string + title: >- + validator_address is the address of the + validator bls_pub_key: type: string format: byte + title: >- + bls_pub_key is the BLS public key of the + validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the + validator at the given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -8280,7 +7586,7 @@ paths: validator_set is the validator set of the sealed epoch This validator set has generated a BLS multisig on - `last_commit_hash` of + `app_hash` of the sealer header proof_epoch_info: @@ -8392,16 +7698,11 @@ paths: already processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains - Babylon checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: >- proof_epoch_submitted is the proof that the epoch's @@ -8529,6 +7830,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -8568,7 +7873,6 @@ paths: name "y.z". - JSON @@ -8690,90 +7994,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and - the rules of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: >- - hashes from the app output from the prev - block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the + babylon block that includes this CZ + + header babylon_epoch: type: string format: uint64 @@ -8828,93 +8064,22 @@ paths: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the + babylon_header_hash is the hash of the + babylon block that includes this CZ + + header + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules - for processing a block in the - blockchain, - - including all blockchain data structures - and the rules of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: >- - hashes from the app output from the prev - block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: >- - Header defines the structure of a block - header. babylon_epoch: type: string format: uint64 @@ -8990,194 +8155,64 @@ paths: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the + time of this epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block + in this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each - epoch to complete + Babylon needs to remember the last header's time of + each epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, + app_hash_root: + type: string + format: byte + title: >- + app_hash_root is the Merkle root of all AppHashs in + this epoch - including all blockchain data structures and the - rules of the application's + It will be used for proving a block is in an epoch + sealer_app_hash: + type: string + format: byte + title: >- + sealer is the last block of the sealed epoch - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - app_hash_root: + sealer_app_hash points to the sealer but stored in + the 1st header + + of the next epoch + sealer_block_hash: type: string format: byte title: >- - app_hash_root is the Merkle root of all AppHashs in - this epoch - - It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, + sealer_block_hash is the hash of the sealer - including all blockchain data structures and the - rules of the application's + the validator set has generated a BLS multisig on + the hash, - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + i.e., hash of the last block in the epoch raw_checkpoint: title: raw_checkpoint is the raw checkpoint of this epoch type: object @@ -9188,14 +8223,14 @@ paths: title: >- epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are + block_hash defines the 'BlockID.Hash', which is the + hash of - signed on + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -9239,62 +8274,35 @@ paths: title: proof is the proof that the chain info is finalized type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries - the header is included + proof_cz_header_in_epoch is the proof that the CZ + header is timestamped - in a certain Babylon block - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of - a transaction in the Merkle tree. - proof_header_in_epoch: + within a certain epoch type: object properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon - header is in a certain + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: >- + ProofOp defines an operation used for + calculating Merkle root - epoch + The data could be arbitrary format, providing + nessecary data + + for example neighbouring node hash proof_epoch_sealed: title: >- proof_epoch_sealed is the proof that the epoch is @@ -9308,12 +8316,21 @@ paths: properties: validator_address: type: string + title: >- + validator_address is the address of the + validator bls_pub_key: type: string format: byte + title: >- + bls_pub_key is the BLS public key of the + validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the + validator at the given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -9324,7 +8341,7 @@ paths: epoch This validator set has generated a BLS multisig - on `last_commit_hash` of + on `app_hash` of the sealer header proof_epoch_info: @@ -9437,8 +8454,7 @@ paths: valideated the proof? title: >- - TransactionInfo is the info of a tx that contains - Babylon checkpoint, + TransactionInfo is the info of a tx on Bitcoin, including @@ -9575,6 +8591,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -9614,7 +8634,6 @@ paths: name "y.z". - JSON @@ -9713,88 +8732,22 @@ paths: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -9961,6 +8914,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -10000,7 +8957,6 @@ paths: name "y.z". - JSON @@ -10144,88 +9100,22 @@ paths: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -10363,6 +9253,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -10402,7 +9296,6 @@ paths: name "y.z". - JSON @@ -10591,6 +9484,10 @@ paths: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -10630,7 +9527,6 @@ paths: name "y.z". - JSON @@ -10742,16 +9638,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: the BTC checkpoint transactions of the best submission best_submission_vigilante_address_list: @@ -10925,16 +9816,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: the BTC checkpoint transactions of the best submission best_submission_vigilante_address_list: @@ -11051,16 +9937,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: the BTC checkpoint transactions of the best submission best_submission_vigilante_address_list: @@ -11322,7 +10203,7 @@ definitions: valideated the proof? title: |- - TransactionInfo is the info of a tx that contains Babylon checkpoint, + TransactionInfo is the info of a tx on Bitcoin, including - the position of the tx on BTC blockchain - the full tx content @@ -11504,6 +10385,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -11539,7 +10424,6 @@ definitions: name "y.z". - JSON @@ -11680,6 +10564,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -11715,7 +10603,6 @@ definitions: name "y.z". - JSON @@ -11775,6 +10662,19 @@ definitions: - Total work spent on the header. This is the sum of the work corresponding to the header Bits field and the total work of the header. + babylon.btclightclient.v1.Params: + type: object + properties: + insert_headers_allow_list: + type: array + items: + type: string + title: >- + List of addresses which are allowed to insert headers to btc light + client + + if the list is empty, any address can insert headers + description: Params defines the parameters for the module. babylon.btclightclient.v1.QueryBaseHeaderResponse: type: object properties: @@ -11858,11 +10758,22 @@ definitions: PageResponse page = 2; } description: QueryHashesResponse is response type for the Query/Hashes RPC method. - babylon.btclightclient.v1.QueryMainChainResponse: + babylon.btclightclient.v1.QueryHeaderDepthResponse: type: object properties: - headers: - type: array + depth: + type: string + format: uint64 + title: >- + QueryMainChainDepthResponse is the response type for the + Query/MainChainDepth RPC + + it contains depth of the block in main chain + babylon.btclightclient.v1.QueryMainChainResponse: + type: object + properties: + headers: + type: array items: type: object properties: @@ -11918,6 +10829,23 @@ definitions: description: >- QueryMainChainResponse is response type for the Query/MainChain RPC method. + babylon.btclightclient.v1.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + insert_headers_allow_list: + type: array + items: + type: string + title: >- + List of addresses which are allowed to insert headers to btc light + client + + if the list is empty, any address can insert headers + description: QueryParamsResponse is the response type for the Query/Params RPC method. babylon.btclightclient.v1.QueryTipResponse: type: object properties: @@ -11993,6 +10921,21 @@ definitions: title: BondState is the bond state of a validator or delegation val_addr: type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. block_height: type: string format: uint64 @@ -12028,6 +10971,18 @@ definitions: title: BondState is the bond state of a validator or delegation val_addr: type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. block_height: type: string format: uint64 @@ -12043,189 +10998,50 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: current_epoch_interval is the epoch interval at the time of this epoch first_block_height: type: string format: uint64 - last_block_header: + title: first_block_height is the height of the first block in this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to complete + Babylon needs to remember the last header's time of each epoch to + complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's - beginning, and + finalised. The last_block_time field is nil in the epoch's beginning, + and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in - the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte title: |- app_hash_root is the Merkle root of all AppHashs in this epoch It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on `last_commit_hash` - of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in - the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + sealer_app_hash: + type: string + format: byte + title: |- + sealer is the last block of the sealed epoch + sealer_app_hash points to the sealer but stored in the 1st header + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch title: Epoch is a structure that contains the metadata of an epoch babylon.epoching.v1.Params: type: object @@ -12283,6 +11099,21 @@ definitions: title: BondState is the bond state of a validator or delegation val_addr: type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. block_height: type: string format: uint64 @@ -12309,191 +11140,53 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time of this + epoch first_block_height: type: string format: uint64 - last_block_header: + title: first_block_height is the height of the first block in this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to + Babylon needs to remember the last header's time of each epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte title: |- app_hash_root is the Merkle root of all AppHashs in this epoch It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + sealer_app_hash: + type: string + format: byte + title: |- + sealer is the last block of the sealed epoch + sealer_app_hash points to the sealer but stored in the 1st header + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch title: Epoch is a structure that contains the metadata of an epoch title: QueryEpochInfoRequest is the response type for the Query/EpochInfo method babylon.epoching.v1.QueryEpochMsgsResponse: @@ -12577,6 +11270,14 @@ definitions: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in MsgCreateValidator + is deprecated. + + The validator address bytes and delegator address bytes + refer to the same account while creating validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -12678,6 +11379,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -12717,7 +11422,6 @@ definitions: name "y.z". - JSON @@ -12858,11 +11562,46 @@ definitions: of coins from a delegator and source validator to a destination validator. + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding delegation + entry balance + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for + performing a cancel unbonding delegation for delegator title: >- QueuedMessage is a message that can change the validator set and is delayed - to the epoch boundary + to the end of an epoch title: msgs is the list of messages queued in the current epoch pagination: title: pagination defines the pagination in the response @@ -12954,191 +11693,58 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time of this + epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block in this + epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to - complete + Babylon needs to remember the last header's time of each epoch + to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte title: |- app_hash_root is the Merkle root of all AppHashs in this epoch It will be used for proving a block is in an epoch - sealer_header: + sealer_app_hash: + type: string + format: byte title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, + sealer is the last block of the sealed epoch - including all blockchain data structures and the rules of - the application's + sealer_app_hash points to the sealer but stored in the 1st + header - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch title: Epoch is a structure that contains the metadata of an epoch pagination: title: pagination defines the pagination in the response @@ -13263,6 +11869,15 @@ definitions: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in + MsgCreateValidator is deprecated. + + The validator address bytes and delegator address + bytes refer to the same account while creating + validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -13364,6 +11979,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -13403,7 +12022,6 @@ definitions: name "y.z". - JSON @@ -13550,11 +12168,47 @@ definitions: of coins from a delegator and source validator to a destination validator. + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding + delegation entry balance + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for + performing a cancel unbonding delegation for delegator title: >- QueuedMessage is a message that can change the validator set and is delayed - to the epoch boundary + to the end of an epoch title: >- QueuedMessageList is a message that contains a list of staking-related @@ -13723,6 +12377,14 @@ definitions: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in MsgCreateValidator is + deprecated. + + The validator address bytes and delegator address bytes refer to + the same account while creating validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -13821,6 +12483,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -13857,7 +12523,6 @@ definitions: name "y.z". - JSON @@ -13989,11 +12654,44 @@ definitions: of coins from a delegator and source validator to a destination validator. + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding delegation entry + balance + creation_height: + type: string + format: int64 + description: creation_height is the height which the unbonding took place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for performing a + cancel unbonding delegation for delegator title: >- QueuedMessage is a message that can change the validator set and is delayed - to the epoch boundary + to the end of an epoch babylon.epoching.v1.QueuedMessageList: type: object properties: @@ -14078,6 +12776,14 @@ definitions: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in MsgCreateValidator + is deprecated. + + The validator address bytes and delegator address bytes + refer to the same account while creating validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -14179,6 +12885,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -14218,7 +12928,6 @@ definitions: name "y.z". - JSON @@ -14359,16 +13068,51 @@ definitions: of coins from a delegator and source validator to a destination validator. - title: >- - QueuedMessage is a message that can change the validator set and is - delayed + msg_cancel_unbonding_delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - to the epoch boundary - title: |- - QueuedMessageList is a message that contains a list of staking-related - messages queued for an epoch - babylon.epoching.v1.ValStateUpdate: - type: object + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding delegation + entry balance + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for + performing a cancel unbonding delegation for delegator + title: >- + QueuedMessage is a message that can change the validator set and is + delayed + + to the end of an epoch + title: |- + QueuedMessageList is a message that contains a list of staking-related + messages queued for an epoch + babylon.epoching.v1.ValStateUpdate: + type: object properties: state: type: string @@ -14522,6 +13266,36 @@ definitions: description: |- MsgBeginRedelegate defines a SDK message for performing a redelegation of coins from a delegator and source validator to a destination validator. + cosmos.staking.v1beta1.MsgCancelUnbondingDelegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + title: >- + amount is always less than or equal to unbonding delegation entry + balance + creation_height: + type: string + format: int64 + description: creation_height is the height which the unbonding took place. + description: 'Since: cosmos-sdk 0.46' + title: >- + MsgCancelUnbondingDelegation defines the SDK message for performing a + cancel unbonding delegation for delegator cosmos.staking.v1beta1.MsgCreateValidator: type: object properties: @@ -14571,6 +13345,14 @@ definitions: type: string delegator_address: type: string + description: >- + Deprecated: Use of Delegator Address in MsgCreateValidator is + deprecated. + + The validator address bytes and delegator address bytes refer to the + same account while creating validator (defer + + only in bech32 notation). validator_address: type: string pubkey: @@ -14665,6 +13447,10 @@ definitions: if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } Example 3: Pack and unpack a message in Python. @@ -14700,7 +13486,6 @@ definitions: name "y.z". - JSON @@ -14791,127 +13576,6 @@ definitions: description: |- MsgUndelegate defines a SDK message for performing an undelegation from a delegate and a validator. - tendermint.types.BlockID: - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - title: BlockID - tendermint.types.Header: - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in the - blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. - tendermint.types.PartSetHeader: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - tendermint.version.Consensus: - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in the - blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. babylon.checkpointing.v1.CheckpointStateUpdate: type: object properties: @@ -14977,12 +13641,17 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the given + epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -15043,14 +13712,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual BLS - sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15065,7 +13732,7 @@ definitions: individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata description: |- QueryLastCheckpointWithStatusResponse is the response type for the Query/LastCheckpointWithStatus RPC method. @@ -15084,14 +13751,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15106,7 +13771,7 @@ definitions: individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -15187,7 +13852,7 @@ definitions: this transition. - description: RawCheckpointWithMeta wraps the raw checkpoint with meta data. + description: RawCheckpointWithMeta wraps the raw checkpoint with metadata. title: the order is going from the newest to oldest based on the epoch number pagination: description: pagination defines the pagination in the response. @@ -15224,14 +13889,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual - BLS sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15246,7 +13909,7 @@ definitions: individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -15325,7 +13988,7 @@ definitions: this transition. - description: RawCheckpointWithMeta wraps the raw checkpoint with meta data. + description: RawCheckpointWithMeta wraps the raw checkpoint with metadata. description: >- QueryRawCheckpointResponse is the response type for the Query/RawCheckpoint @@ -15346,14 +14009,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15368,7 +14029,7 @@ definitions: individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -15449,7 +14110,7 @@ definitions: this transition. - description: RawCheckpointWithMeta wraps the raw checkpoint with meta data. + description: RawCheckpointWithMeta wraps the raw checkpoint with metadata. title: the order is going from the newest to oldest based on the epoch number pagination: description: pagination defines the pagination in the response. @@ -15499,14 +14160,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual BLS sigs - are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15521,7 +14180,7 @@ definitions: BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata babylon.checkpointing.v1.RawCheckpointWithMeta: type: object properties: @@ -15532,14 +14191,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual BLS - sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -15554,7 +14211,7 @@ definitions: individual BLS sigs - title: RawCheckpoint wraps the BLS multi sig with meta data + title: RawCheckpoint wraps the BLS multi sig with metadata status: type: string enum: @@ -15625,18 +14282,21 @@ definitions: lifecycle defines the lifecycle of this checkpoint, i.e., each state transition and the time (in both timestamp and block height) of this transition. - description: RawCheckpointWithMeta wraps the raw checkpoint with meta data. + description: RawCheckpointWithMeta wraps the raw checkpoint with metadata. babylon.checkpointing.v1.ValidatorWithBlsKey: type: object properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: voting_power is the voting power of the validator at the given epoch title: |- ValidatorWithBlsKey couples validator address, voting power, and its bls public key @@ -15671,88 +14331,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -15801,88 +14395,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules - of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -15981,88 +14509,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -16112,88 +14574,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -16267,191 +14663,53 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time of this + epoch first_block_height: type: string format: uint64 - last_block_header: + title: first_block_height is the height of the first block in this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to + Babylon needs to remember the last header's time of each epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte title: |- app_hash_root is the Merkle root of all AppHashs in this epoch It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + sealer_app_hash: + type: string + format: byte + title: |- + sealer is the last block of the sealed epoch + sealer_app_hash points to the sealer but stored in the 1st header + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch raw_checkpoint: title: raw_checkpoint is the raw checkpoint of this epoch type: object @@ -16460,14 +14718,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual BLS - sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -16509,62 +14765,34 @@ definitions: title: proof is the proof that the chain info is finalized type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries the header is - included + proof_cz_header_in_epoch is the proof that the CZ header is + timestamped - in a certain Babylon block + within a certain epoch type: object properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a transaction - in the Merkle tree. - proof_header_in_epoch: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon header is in a - certain + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: >- + ProofOp defines an operation used for calculating Merkle + root - epoch + The data could be arbitrary format, providing nessecary data + + for example neighbouring node hash proof_epoch_sealed: title: proof_epoch_sealed is the proof that the epoch is sealed type: object @@ -16576,12 +14804,17 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the + given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -16590,8 +14823,8 @@ definitions: title: >- validator_set is the validator set of the sealed epoch - This validator set has generated a BLS multisig on - `last_commit_hash` of + This validator set has generated a BLS multisig on `app_hash` + of the sealer header proof_epoch_info: @@ -16703,16 +14936,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: >- proof_epoch_submitted is the proof that the epoch's checkpoint is @@ -16753,88 +14981,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes - this CZ + babylon_header_hash is the hash of the babylon block that + includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -16902,88 +15064,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes this - CZ + babylon_header_hash is the hash of the babylon block that includes + this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in - the blockchain, - - including all blockchain data structures and the rules of the - application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that includes + this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -17021,23 +15117,25 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the given + epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls public key - title: >- + title: |- validator_set is the validator set of the sealed epoch - - This validator set has generated a BLS multisig on `last_commit_hash` - of - + This validator set has generated a BLS multisig on `app_hash` of the sealer header proof_epoch_info: title: >- @@ -17099,11 +15197,11 @@ definitions: The verifier can perform the following verification rules: - - The raw checkpoint's `last_commit_hash` is same as in the sealer header + - The raw checkpoint's `app_hash` is same as in the sealer header - - More than 1/3 (in voting power) validators in the validator set of this + - More than 2/3 (in voting power) validators in the validator set of this - epoch have signed `last_commit_hash` of the sealer header + epoch have signed `app_hash` of the sealer header - The epoch medatata is committed to the `app_hash` of the sealer header @@ -17111,62 +15209,31 @@ definitions: babylon.zoneconcierge.v1.ProofFinalizedChainInfo: type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries the header is - included + proof_cz_header_in_epoch is the proof that the CZ header is + timestamped - in a certain Babylon block - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a transaction in - the Merkle tree. - proof_header_in_epoch: + within a certain epoch type: object properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon header is in a - certain - - epoch + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: |- + ProofOp defines an operation used for calculating Merkle root + The data could be arbitrary format, providing nessecary data + for example neighbouring node hash proof_epoch_sealed: title: proof_epoch_sealed is the proof that the epoch is sealed type: object @@ -17178,23 +15245,25 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the + given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls public key - title: >- + title: |- validator_set is the validator set of the sealed epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - + This validator set has generated a BLS multisig on `app_hash` of the sealer header proof_epoch_info: title: >- @@ -17303,16 +15372,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: >- proof_epoch_submitted is the proof that the epoch's checkpoint is @@ -17389,99 +15453,33 @@ definitions: height is the height of this header on CZ ledger (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 CZ - - header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules - of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block 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_hash: + type: string + format: byte + title: >- + babylon_header_hash is the hash of the babylon block that + includes this CZ + + header + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ + + header babylon_epoch: type: string format: uint64 @@ -17532,88 +15530,22 @@ definitions: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -17722,88 +15654,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules - of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -17854,88 +15720,22 @@ definitions: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -18026,107 +15826,41 @@ definitions: hash: type: string format: byte - title: hash is the hash of this header - height: - type: string - format: uint64 - title: >- - height is the height of this header on CZ ledger - - (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 CZ - - header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + title: hash is the hash of this header + height: + type: string + format: uint64 + title: >- + height is the height of this header on CZ ledger + + (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_hash: + type: string + format: byte + title: >- + babylon_header_hash is the hash of the babylon block that + includes this CZ + + header + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ + + header babylon_epoch: type: string format: uint64 @@ -18176,88 +15910,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -18331,191 +15999,53 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time of this + epoch first_block_height: type: string format: uint64 - last_block_header: + title: first_block_height is the height of the first block in this epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to + Babylon needs to remember the last header's time of each epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte title: |- app_hash_root is the Merkle root of all AppHashs in this epoch It will be used for proving a block is in an epoch - sealer_header: - title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + sealer_app_hash: + type: string + format: byte + title: |- + sealer is the last block of the sealed epoch + sealer_app_hash points to the sealer but stored in the 1st header + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch raw_checkpoint: title: raw_checkpoint is the raw checkpoint of this epoch type: object @@ -18524,14 +16054,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that individual BLS - sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -18573,62 +16101,34 @@ definitions: title: proof is the proof that the chain info is finalized type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries the header is - included + proof_cz_header_in_epoch is the proof that the CZ header is + timestamped - in a certain Babylon block - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a transaction - in the Merkle tree. - proof_header_in_epoch: + within a certain epoch type: object properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon header is in a - certain + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: >- + ProofOp defines an operation used for calculating Merkle + root - epoch + The data could be arbitrary format, providing nessecary data + + for example neighbouring node hash proof_epoch_sealed: title: proof_epoch_sealed is the proof that the epoch is sealed type: object @@ -18640,12 +16140,17 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator at the + given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -18654,8 +16159,8 @@ definitions: title: >- validator_set is the validator set of the sealed epoch - This validator set has generated a BLS multisig on - `last_commit_hash` of + This validator set has generated a BLS multisig on `app_hash` + of the sealer header proof_epoch_info: @@ -18767,16 +16272,11 @@ definitions: processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: >- proof_epoch_submitted is the proof that the epoch's checkpoint is @@ -18834,88 +16334,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that - includes this CZ + babylon_header_hash is the hash of the babylon block + that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and the - rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -18968,88 +16402,22 @@ definitions: before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block - that includes this CZ + babylon_header_hash is the hash of the babylon + block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for - processing a block in the blockchain, - - including all blockchain data structures and - the rules of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon + block that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -19123,102 +16491,35 @@ definitions: epoch_number: type: string format: uint64 + title: epoch_number is the number of this epoch current_epoch_interval: type: string format: uint64 + title: >- + current_epoch_interval is the epoch interval at the time of + this epoch first_block_height: type: string format: uint64 - last_block_header: + title: >- + first_block_height is the height of the first block in this + epoch + last_block_time: + type: string + format: date-time description: >- - last_block_header is the header of the last block in this - epoch. + last_block_time is the time of the last block in this epoch. - Babylon needs to remember the last header of each epoch to - complete + Babylon needs to remember the last header's time of each + epoch to complete unbonding validators/delegations when a previous epoch's checkpoint is - finalised. The last_block_header field is nil in the epoch's + finalised. The last_block_time field is nil in the epoch's beginning, and is set upon the end of this epoch. - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules - of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte app_hash_root: type: string format: byte @@ -19227,90 +16528,23 @@ definitions: epoch It will be used for proving a block is in an epoch - sealer_header: + sealer_app_hash: + type: string + format: byte title: >- - sealer_header is the 2nd header of the next epoch - - This validator set has generated a BLS multisig on - `last_commit_hash` of - - the sealer header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, + sealer is the last block of the sealed epoch - including all blockchain data structures and the rules - of the application's + sealer_app_hash points to the sealer but stored in the 1st + header - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + of the next epoch + sealer_block_hash: + type: string + format: byte + title: |- + sealer_block_hash is the hash of the sealer + the validator set has generated a BLS multisig on the hash, + i.e., hash of the last block in the epoch raw_checkpoint: title: raw_checkpoint is the raw checkpoint of this epoch type: object @@ -19319,14 +16553,12 @@ definitions: type: string format: uint64 title: epoch_num defines the epoch number the raw checkpoint is for - last_commit_hash: + block_hash: type: string format: byte - title: >- - last_commit_hash defines the 'LastCommitHash' that - individual BLS sigs are - - signed on + title: |- + block_hash defines the 'BlockID.Hash', which is the hash of + the block that individual BLS sigs are signed on bitmap: type: string format: byte @@ -19370,62 +16602,35 @@ definitions: title: proof is the proof that the chain info is finalized type: object properties: - proof_tx_in_block: + proof_cz_header_in_epoch: title: >- - proof_tx_in_block is the proof that tx that carries the - header is included + proof_cz_header_in_epoch is the proof that the CZ header is + timestamped - in a certain Babylon block - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a - transaction in the Merkle tree. - proof_header_in_epoch: + within a certain epoch type: object properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: + ops: type: array items: - type: string - format: byte - title: >- - proof_header_in_epoch is the proof that the Babylon header - is in a certain + type: object + properties: + type: + type: string + key: + type: string + format: byte + data: + type: string + format: byte + title: >- + ProofOp defines an operation used for calculating + Merkle root - epoch + The data could be arbitrary format, providing + nessecary data + + for example neighbouring node hash proof_epoch_sealed: title: proof_epoch_sealed is the proof that the epoch is sealed type: object @@ -19437,12 +16642,17 @@ definitions: properties: validator_address: type: string + title: validator_address is the address of the validator bls_pub_key: type: string format: byte + title: bls_pub_key is the BLS public key of the validator voting_power: type: string format: uint64 + title: >- + voting_power is the voting power of the validator + at the given epoch title: >- ValidatorWithBlsKey couples validator address, voting power, and its bls @@ -19452,7 +16662,7 @@ definitions: validator_set is the validator set of the sealed epoch This validator set has generated a BLS multisig on - `last_commit_hash` of + `app_hash` of the sealer header proof_epoch_info: @@ -19564,16 +16774,11 @@ definitions: already processed and valideated the proof? - title: >- - TransactionInfo is the info of a tx that contains Babylon - checkpoint, - + title: |- + TransactionInfo is the info of a tx on Bitcoin, including - - the position of the tx on BTC blockchain - - the full tx content - - the Merkle proof that this tx is on the above position title: >- proof_epoch_submitted is the proof that the epoch's @@ -19615,88 +16820,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block - in the blockchain, - - including all blockchain data structures and the rules of the - application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -19745,88 +16884,22 @@ definitions: validators/delegations before this timestamp when this header is BTC-finalised - babylon_header: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that + babylon_header_hash is the hash of the babylon block that includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules - of the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block + that includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -19904,88 +16977,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes - this CZ + babylon_header_hash is the hash of the babylon block that + includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -20037,88 +17044,22 @@ definitions: 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: + babylon_header_hash: + type: string + format: byte title: >- - babylon_header is the header of the babylon block that includes - this CZ + babylon_header_hash is the hash of the babylon block that + includes this CZ header - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a - block in the blockchain, - - including all blockchain data structures and the rules of - the application's + babylon_header_height: + type: string + format: uint64 + title: >- + babylon_header_height is the height of the babylon block that + includes this CZ - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. + header babylon_epoch: type: string format: uint64 @@ -20181,23 +17122,6 @@ definitions: 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: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte tendermint.crypto.ProofOp: type: object properties: @@ -20234,32 +17158,3 @@ definitions: The data could be arbitrary format, providing nessecary data for example neighbouring node hash title: ProofOps is Merkle proof defined by the list of ProofOps - tendermint.types.TxProof: - type: object - properties: - root_hash: - type: string - format: byte - data: - type: string - format: byte - proof: - type: object - properties: - total: - type: string - format: int64 - index: - type: string - format: int64 - leaf_hash: - type: string - format: byte - aunts: - type: array - items: - type: string - format: byte - description: >- - TxProof represents a Merkle proof of the presence of a transaction in the - Merkle tree. diff --git a/client/tx/tx.go b/client/tx/tx.go index cc7b9b4e3..97128e375 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -1,6 +1,7 @@ package tx import ( + "context" "errors" "fmt" @@ -11,17 +12,11 @@ import ( "github.com/babylonchain/babylon/types" ) -func SendMsgToTendermint(clientCtx client.Context, msg sdk.Msg) (*sdk.TxResponse, error) { - return SendMsgsToTendermint(clientCtx, []sdk.Msg{msg}) +func SendMsgToComet(ctx context.Context, clientCtx client.Context, msg sdk.Msg) (*sdk.TxResponse, error) { + return SendMsgsToComet(ctx, clientCtx, []sdk.Msg{msg}) } -func SendMsgsToTendermint(clientCtx client.Context, msgs []sdk.Msg) (*sdk.TxResponse, error) { - for _, msg := range msgs { - if err := msg.ValidateBasic(); err != nil { - return nil, err - } - } - +func SendMsgsToComet(ctx context.Context, clientCtx client.Context, msgs []sdk.Msg) (*sdk.TxResponse, error) { gasPrice, gasAdjustment := types.MustGetGasSettings(clientCtx.HomeDir, clientCtx.Viper) txf := sdktx.Factory{}. WithTxConfig(clientCtx.TxConfig). @@ -32,7 +27,7 @@ func SendMsgsToTendermint(clientCtx client.Context, msgs []sdk.Msg) (*sdk.TxResp WithGasPrices(gasPrice). WithGasAdjustment(gasAdjustment) - return BroadcastTx(clientCtx, txf, msgs...) + return BroadcastTx(ctx, clientCtx, txf, msgs...) } // BroadcastTx attempts to generate, sign and broadcast a transaction with the @@ -40,7 +35,7 @@ func SendMsgsToTendermint(clientCtx client.Context, msgs []sdk.Msg) (*sdk.TxResp // It will return an error upon failure. // The code is based on cosmos-sdk: https://github.com/cosmos/cosmos-sdk/blob/7781cdb3d20bc7ebac017452897ce1e6ab3903ef/client/tx/tx.go#L65 // it treats non-zero response code as errors -func BroadcastTx(clientCtx client.Context, txf sdktx.Factory, msgs ...sdk.Msg) (*sdk.TxResponse, error) { +func BroadcastTx(ctx context.Context, clientCtx client.Context, txf sdktx.Factory, msgs ...sdk.Msg) (*sdk.TxResponse, error) { txf, err := prepareFactory(clientCtx, txf) if err != nil { return nil, err @@ -64,7 +59,7 @@ func BroadcastTx(clientCtx client.Context, txf sdktx.Factory, msgs ...sdk.Msg) ( tx.SetFeeGranter(clientCtx.GetFeeGranterAddress()) - err = sdktx.Sign(txf, clientCtx.GetFromName(), tx, true) + err = sdktx.Sign(ctx, txf, clientCtx.GetFromName(), tx, true) if err != nil { return nil, err } @@ -74,7 +69,7 @@ func BroadcastTx(clientCtx client.Context, txf sdktx.Factory, msgs ...sdk.Msg) ( return nil, err } - // broadcast to a Tendermint node + // broadcast to a comet node res, err := clientCtx.BroadcastTx(txBytes) if err != nil { return nil, err diff --git a/cmd/babylond/cmd/add_gen_bls.go b/cmd/babylond/cmd/add_gen_bls.go index f4af2c504..a55e17736 100644 --- a/cmd/babylond/cmd/add_gen_bls.go +++ b/cmd/babylond/cmd/add_gen_bls.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - tmos "github.com/cometbft/cometbft/libs/os" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/x/genutil" @@ -32,7 +32,7 @@ BLS keys in the checkpointing module's genesis state.' // load genesis BLS key genKeyFilePath := args[0] - if !tmos.FileExists(genKeyFilePath) { + if !cmtos.FileExists(genKeyFilePath) { return errors.New("genesis BLS key file does not exist") } genKey, err := types.LoadGenesisKeyFromFile(genKeyFilePath) diff --git a/cmd/babylond/cmd/add_gen_bls_test.go b/cmd/babylond/cmd/add_gen_bls_test.go index 0bb59cfb7..f261c2c09 100644 --- a/cmd/babylond/cmd/add_gen_bls_test.go +++ b/cmd/babylond/cmd/add_gen_bls_test.go @@ -6,16 +6,23 @@ import ( "path/filepath" "testing" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/x/genutil" - tmconfig "github.com/cometbft/cometbft/config" + appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" + authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1" + "cosmossdk.io/core/appconfig" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + cmtconfig "github.com/cometbft/cometbft/config" tmjson "github.com/cometbft/cometbft/libs/json" - "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/libs/tempfile" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/testutil/configurator" "github.com/cosmos/cosmos-sdk/testutil/network" genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -30,21 +37,59 @@ import ( "github.com/babylonchain/babylon/x/checkpointing/types" ) +func newConfig() depinject.Config { + return configurator.NewAppConfig( + func(config *configurator.Config) { + config.ModuleConfigs["auth"] = &appv1alpha1.ModuleConfig{ + Name: "auth", + Config: appconfig.WrapAny(&authmodulev1.Module{ + Bech32Prefix: "bbn", // overwrite prefix here + ModuleAccountPermissions: []*authmodulev1.ModuleAccountPermission{ + {Account: "fee_collector"}, + {Account: "distribution"}, + {Account: "mint", Permissions: []string{"minter"}}, + {Account: "bonded_tokens_pool", Permissions: []string{"burner", "staking"}}, + {Account: "not_bonded_tokens_pool", Permissions: []string{"burner", "staking"}}, + {Account: "gov", Permissions: []string{"burner"}}, + {Account: "nft"}, + }, + }), + } + }, + configurator.ParamsModule(), + configurator.BankModule(), + configurator.GenutilModule(), + configurator.StakingModule(), + configurator.ConsensusModule(), + configurator.TxModule(), + ) +} + // test adding genesis BLS keys without gentx // error is expected func Test_AddGenBlsCmdWithoutGentx(t *testing.T) { home := t.TempDir() logger := log.NewNopLogger() - tmcfg, err := genutiltest.CreateDefaultTendermintConfig(home) + cmtcfg, err := genutiltest.CreateDefaultCometConfig(home) require.NoError(t, err) - appCodec := app.GetEncodingConfig().Marshaler - gentxModule := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) + db := dbm.NewMemDB() + signer, err := app.SetupTestPrivSigner() + require.NoError(t, err) + bbn := app.NewBabylonAppWithCustomOptions(t, false, signer, app.SetupOptions{ + Logger: logger, + DB: db, + InvCheckPeriod: 0, + SkipUpgradeHeights: map[int64]bool{}, + AppOpts: app.EmptyAppOptions{}, + }) + gentxModule := bbn.BasicModuleManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) + appCodec := bbn.AppCodec() err = genutiltest.ExecInitCmd(testMbm, home, appCodec) require.NoError(t, err) - serverCtx := server.NewContext(viper.New(), tmcfg, logger) + serverCtx := server.NewContext(viper.New(), cmtcfg, logger) clientCtx := client.Context{}.WithCodec(appCodec).WithHomeDir(home) cfg := serverCtx.Config cfg.SetRoot(clientCtx.HomeDir) @@ -70,20 +115,29 @@ func Test_AddGenBlsCmdWithoutGentx(t *testing.T) { // test adding genesis BLS keys with gentx // error is expected if adding duplicate func Test_AddGenBlsCmdWithGentx(t *testing.T) { - min := network.MinimumAppConfig() - cfg, _ := network.DefaultConfigWithAppConfig(min) + db := dbm.NewMemDB() + signer, err := app.SetupTestPrivSigner() + require.NoError(t, err) + bbn := app.NewBabylonAppWithCustomOptions(t, false, signer, app.SetupOptions{ + Logger: log.NewNopLogger(), + DB: db, + InvCheckPeriod: 0, + SkipUpgradeHeights: map[int64]bool{}, + AppOpts: app.EmptyAppOptions{}, + }) + + gentxModule := bbn.BasicModuleManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) config.SetConfigTemplate(config.DefaultConfigTemplate) + cfg, _ := network.DefaultConfigWithAppConfig(newConfig()) cfg.NumValidators = 1 - testNetwork, err := network.New(t, t.TempDir(), cfg) require.NoError(t, err) defer testNetwork.Cleanup() _, err = testNetwork.WaitForHeight(1) require.NoError(t, err) - gentxModule := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) - targetCfg := tmconfig.DefaultConfig() + targetCfg := cmtconfig.DefaultConfig() targetCfg.SetRoot(filepath.Join(testNetwork.Validators[0].Dir, "simd")) targetGenesisFile := targetCfg.GenesisFile() targetCtx := testNetwork.Validators[0].ClientCtx @@ -91,13 +145,12 @@ func Test_AddGenBlsCmdWithGentx(t *testing.T) { v := testNetwork.Validators[i] // build and create genesis BLS key genBlsCmd := cmd.GenBlsCmd() - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() homeDir := filepath.Join(v.Dir, "simd") nodeCfg.SetRoot(homeDir) keyPath := nodeCfg.PrivValidatorKeyFile() statePath := nodeCfg.PrivValidatorStateFile() filePV := privval.GenWrappedFilePV(keyPath, statePath) - defer filePV.Clean(keyPath, statePath) filePV.SetAccAddress(v.Address) _, err = cli.ExecTestCLICmd(v.ClientCtx, genBlsCmd, []string{fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir)}) require.NoError(t, err) @@ -120,5 +173,6 @@ func Test_AddGenBlsCmdWithGentx(t *testing.T) { require.NotEmpty(t, checkpointingGenState.GenesisKeys) gks := checkpointingGenState.GetGenesisKeys() require.Equal(t, genKey, gks[i]) + filePV.Clean(keyPath, statePath) } } diff --git a/cmd/babylond/cmd/cmd_test.go b/cmd/babylond/cmd/cmd_test.go index b91efe3f2..f00b10994 100644 --- a/cmd/babylond/cmd/cmd_test.go +++ b/cmd/babylond/cmd/cmd_test.go @@ -13,7 +13,7 @@ import ( ) func TestInitCmd(t *testing.T) { - rootCmd, _ := cmd.NewRootCmd() + rootCmd := cmd.NewRootCmd() rootCmd.SetArgs([]string{ "init", // Test the init cmd "app-test", // Moniker diff --git a/cmd/babylond/cmd/create_bls_key.go b/cmd/babylond/cmd/create_bls_key.go index 2d2430e2c..d72c5301d 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -3,17 +3,19 @@ package cmd import ( "errors" "fmt" + "path/filepath" + "strings" + + cmtconfig "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + "github.com/babylonchain/babylon/app" appparams "github.com/babylonchain/babylon/app/params" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/privval" - tmconfig "github.com/cometbft/cometbft/config" - tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cosmos/cosmos-sdk/client/flags" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - "path/filepath" - "strings" ) func CreateBlsKeyCmd() *cobra.Command { @@ -55,10 +57,10 @@ $ babylond create-bls-key %s1f5tnl46mk4dfp4nx3n2vnrvyw2h2ydz6ykhk3r --home ./ } func CreateBlsKey(home string, addr sdk.AccAddress) error { - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) - if !tmos.FileExists(keyPath) { + if !cmtos.FileExists(keyPath) { return errors.New("validator key file does not exist") } pv := privval.LoadWrappedFilePV(keyPath, statePath) diff --git a/cmd/babylond/cmd/custom_babylon_config.go b/cmd/babylond/cmd/custom_babylon_config.go index dea8fd351..c90ec3210 100644 --- a/cmd/babylond/cmd/custom_babylon_config.go +++ b/cmd/babylond/cmd/custom_babylon_config.go @@ -7,12 +7,6 @@ import ( bbn "github.com/babylonchain/babylon/types" ) -const ( - defaultKeyName = "" - defaultGasPrice = "0.01ubbn" - defaultGasAdjustment = 1.5 -) - type BtcConfig struct { Network string `mapstructure:"network"` } @@ -23,36 +17,19 @@ func defaultBabylonBtcConfig() BtcConfig { } } -func defaultSignerConfig() SignerConfig { - return SignerConfig{ - KeyName: defaultKeyName, - GasPrice: defaultGasPrice, - GasAdjustment: defaultGasAdjustment, - } -} - -type SignerConfig struct { - KeyName string `mapstructure:"key-name"` - GasPrice string `mapstructure:"gas-price"` - GasAdjustment float64 `mapstructure:"gas-adjustment"` -} - type BabylonAppConfig struct { serverconfig.Config `mapstructure:",squash"` Wasm wasmtypes.WasmConfig `mapstructure:"wasm"` BtcConfig BtcConfig `mapstructure:"btc-config"` - - SignerConfig SignerConfig `mapstructure:"signer-config"` } func DefaultBabylonConfig() *BabylonAppConfig { return &BabylonAppConfig{ - Config: *serverconfig.DefaultConfig(), - Wasm: wasmtypes.DefaultWasmConfig(), - BtcConfig: defaultBabylonBtcConfig(), - SignerConfig: defaultSignerConfig(), + Config: *serverconfig.DefaultConfig(), + Wasm: wasmtypes.DefaultWasmConfig(), + BtcConfig: defaultBabylonBtcConfig(), } } @@ -65,16 +42,7 @@ func DefaultBabylonTemplate() string { [btc-config] # Configures which bitcoin network should be used for checkpointing -# valid values are: [mainnet, testnet, simnet, regtest] +# valid values are: [mainnet, testnet, simnet, signet, regtest] network = "{{ .BtcConfig.Network }}" - -[signer-config] - -# Configures which key that the BLS signer uses to sign BLS-sig transactions -key-name = "{{ .SignerConfig.KeyName }}" -# Configures the gas-price that the signer would like to pay -gas-price = "{{ .SignerConfig.GasPrice }}" -# Configures the adjustment of the gas cost of estimation -gas-adjustment = "{{ .SignerConfig.GasAdjustment }}" ` } diff --git a/cmd/babylond/cmd/flags.go b/cmd/babylond/cmd/flags.go index 0e24553ec..ee7c61488 100644 --- a/cmd/babylond/cmd/flags.go +++ b/cmd/babylond/cmd/flags.go @@ -1,48 +1,76 @@ package cmd import ( + "strings" "time" - babylonApp "github.com/babylonchain/babylon/app" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + "cosmossdk.io/math" + tmrand "github.com/cometbft/cometbft/libs/rand" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" + + babylonApp "github.com/babylonchain/babylon/app" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btcltypes "github.com/babylonchain/babylon/x/btclightclient/types" + btcstypes "github.com/babylonchain/babylon/x/btcstaking/types" ) const ( - flagMaxActiveValidators = "max-active-validators" - flagBtcConfirmationDepth = "btc-confirmation-depth" - flagEpochInterval = "epoch-interval" - flagBtcFinalizationTimeout = "btc-finalization-timeout" - flagCheckpointTag = "checkpoint-tag" - flagBaseBtcHeaderHex = "btc-base-header" - flagBaseBtcHeaderHeight = "btc-base-header-height" - flagInflationRateChange = "inflation-rate-change" - flagInflationMax = "inflation-max" - flagInflationMin = "inflation-min" - flagGoalBonded = "goal-bonded" - flagBlocksPerYear = "blocks-per-year" - flagGenesisTime = "genesis-time" - flagBlockGasLimit = "block-gas-limit" + flagMaxActiveValidators = "max-active-validators" + flagBtcConfirmationDepth = "btc-confirmation-depth" + flagEpochInterval = "epoch-interval" + flagBtcFinalizationTimeout = "btc-finalization-timeout" + flagCheckpointTag = "checkpoint-tag" + flagBaseBtcHeaderHex = "btc-base-header" + flagBaseBtcHeaderHeight = "btc-base-header-height" + flagAllowedReporterAddresses = "allowed-reporter-addresses" + flagInflationRateChange = "inflation-rate-change" + flagInflationMax = "inflation-max" + flagInflationMin = "inflation-min" + flagGoalBonded = "goal-bonded" + flagBlocksPerYear = "blocks-per-year" + flagGenesisTime = "genesis-time" + flagBlockGasLimit = "block-gas-limit" + flagVoteExtensionEnableHeight = "vote-extension-enable-height" + flagCovenantPks = "covenant-pks" + flagCovenantQuorum = "covenant-quorum" + flagMaxActiveFinalityProviders = "max-active-finality-providers" + flagMinUnbondingTime = "min-unbonding-time" + flagSlashingAddress = "slashing-address" + flagMinSlashingFee = "min-slashing-fee-sat" + flagSlashingRate = "slashing-rate" + flagMinPubRand = "min-pub-rand" + flagMinCommissionRate = "min-commission-rate" ) type GenesisCLIArgs struct { - ChainID string - MaxActiveValidators uint32 - BtcConfirmationDepth uint64 - BtcFinalizationTimeout uint64 - CheckpointTag string - EpochInterval uint64 - BaseBtcHeaderHex string - BaseBtcHeaderHeight uint64 - InflationRateChange float64 - InflationMax float64 - InflationMin float64 - GoalBonded float64 - BlocksPerYear uint64 - GenesisTime time.Time - BlockGasLimit int64 + ChainID string + MaxActiveValidators uint32 + BtcConfirmationDepth uint64 + BtcFinalizationTimeout uint64 + CheckpointTag string + EpochInterval uint64 + BaseBtcHeaderHex string + BaseBtcHeaderHeight uint64 + AllowedReporterAddresses []string + InflationRateChange float64 + InflationMax float64 + InflationMin float64 + GoalBonded float64 + BlocksPerYear uint64 + GenesisTime time.Time + BlockGasLimit int64 + VoteExtensionEnableHeight int64 + CovenantPKs []string + CovenantQuorum uint32 + SlashingAddress string + MinSlashingTransactionFeeSat int64 + SlashingRate math.LegacyDec + MaxActiveFinalityProviders uint32 + MinUnbondingTime uint16 + MinPubRand uint64 + MinCommissionRate math.LegacyDec } func addGenesisFlags(cmd *cobra.Command) { @@ -58,14 +86,30 @@ func addGenesisFlags(cmd *cobra.Command) { // btclightclient args // Genesis header for the simnet cmd.Flags().String(flagBaseBtcHeaderHex, "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a45068653ffff7f2002000000", "Hex of the base Bitcoin header.") + cmd.Flags().String(flagAllowedReporterAddresses, strings.Join(btcltypes.DefaultParams().InsertHeadersAllowList, ","), "addresses of reporters allowed to submit Bitcoin headers to babylon") cmd.Flags().Uint64(flagBaseBtcHeaderHeight, 0, "Height of the base Bitcoin header.") + // btcstaking args + cmd.Flags().String(flagCovenantPks, strings.Join(btcstypes.DefaultParams().CovenantPksHex(), ","), "Bitcoin staking covenant public keys, comma separated") + cmd.Flags().Uint32(flagCovenantQuorum, btcstypes.DefaultParams().CovenantQuorum, "Bitcoin staking covenant quorum") + cmd.Flags().String(flagSlashingAddress, btcstypes.DefaultParams().SlashingAddress, "Bitcoin staking slashing address") + cmd.Flags().Int64(flagMinSlashingFee, 1000, "Bitcoin staking minimum slashing fee") + cmd.Flags().String(flagMinCommissionRate, "0", "Bitcoin staking validator minimum commission rate") + cmd.Flags().String(flagSlashingRate, "0.1", "Bitcoin staking slashing rate") + cmd.Flags().Uint32(flagMaxActiveFinalityProviders, 100, "Bitcoin staking maximum active finality providers") + cmd.Flags().Uint16(flagMinUnbondingTime, 0, "Min timelock on unbonding transaction in btc blocks") + // finality args + cmd.Flags().Uint64(flagMinPubRand, 100, "Bitcoin staking minimum public randomness commit") + // inflation args cmd.Flags().Float64(flagInflationRateChange, 0.13, "Inflation rate change") cmd.Flags().Float64(flagInflationMax, 0.2, "Maximum inflation") cmd.Flags().Float64(flagInflationMin, 0.07, "Minimum inflation") cmd.Flags().Float64(flagGoalBonded, 0.67, "Bonded tokens goal") cmd.Flags().Uint64(flagBlocksPerYear, 6311520, "Blocks per year") + // genesis args cmd.Flags().Int64(flagGenesisTime, time.Now().Unix(), "Genesis time") + // blocks args cmd.Flags().Int64(flagBlockGasLimit, babylonApp.DefaultGasLimit, "Block gas limit") + cmd.Flags().Int64(flagVoteExtensionEnableHeight, babylonApp.DefaultVoteExtensionsEnableHeight, "Vote extension enable height") } func parseGenesisFlags(cmd *cobra.Command) *GenesisCLIArgs { @@ -77,6 +121,16 @@ func parseGenesisFlags(cmd *cobra.Command) *GenesisCLIArgs { epochInterval, _ := cmd.Flags().GetUint64(flagEpochInterval) baseBtcHeaderHex, _ := cmd.Flags().GetString(flagBaseBtcHeaderHex) baseBtcHeaderHeight, _ := cmd.Flags().GetUint64(flagBaseBtcHeaderHeight) + reporterAddresses, _ := cmd.Flags().GetString(flagAllowedReporterAddresses) + covenantPks, _ := cmd.Flags().GetString(flagCovenantPks) + covenantQuorum, _ := cmd.Flags().GetUint32(flagCovenantQuorum) + slashingAddress, _ := cmd.Flags().GetString(flagSlashingAddress) + minSlashingFee, _ := cmd.Flags().GetInt64(flagMinSlashingFee) + minCommissionRate, _ := cmd.Flags().GetString(flagMinCommissionRate) + slashingRate, _ := cmd.Flags().GetString(flagSlashingRate) + maxActiveFinalityProviders, _ := cmd.Flags().GetUint32(flagMaxActiveFinalityProviders) + minUnbondingTime, _ := cmd.Flags().GetUint16(flagMinUnbondingTime) + minPubRand, _ := cmd.Flags().GetUint64(flagMinPubRand) genesisTimeUnix, _ := cmd.Flags().GetInt64(flagGenesisTime) inflationRateChange, _ := cmd.Flags().GetFloat64(flagInflationRateChange) inflationMax, _ := cmd.Flags().GetFloat64(flagInflationMax) @@ -84,28 +138,45 @@ func parseGenesisFlags(cmd *cobra.Command) *GenesisCLIArgs { goalBonded, _ := cmd.Flags().GetFloat64(flagGoalBonded) blocksPerYear, _ := cmd.Flags().GetUint64(flagBlocksPerYear) blockGasLimit, _ := cmd.Flags().GetInt64(flagBlockGasLimit) + voteExtensionEnableHeight, _ := cmd.Flags().GetInt64(flagVoteExtensionEnableHeight) if chainID == "" { chainID = "chain-" + tmrand.NewRand().Str(6) } + var allowedReporterAddresses []string = make([]string, 0) + if reporterAddresses != "" { + allowedReporterAddresses = strings.Split(reporterAddresses, ",") + } + genesisTime := time.Unix(genesisTimeUnix, 0) return &GenesisCLIArgs{ - ChainID: chainID, - MaxActiveValidators: maxActiveValidators, - BtcConfirmationDepth: btcConfirmationDepth, - BtcFinalizationTimeout: btcFinalizationTimeout, - CheckpointTag: checkpointTag, - EpochInterval: epochInterval, - BaseBtcHeaderHeight: baseBtcHeaderHeight, - BaseBtcHeaderHex: baseBtcHeaderHex, - GenesisTime: genesisTime, - InflationRateChange: inflationRateChange, - InflationMax: inflationMax, - InflationMin: inflationMin, - GoalBonded: goalBonded, - BlocksPerYear: blocksPerYear, - BlockGasLimit: blockGasLimit, + ChainID: chainID, + MaxActiveValidators: maxActiveValidators, + BtcConfirmationDepth: btcConfirmationDepth, + BtcFinalizationTimeout: btcFinalizationTimeout, + CheckpointTag: checkpointTag, + EpochInterval: epochInterval, + BaseBtcHeaderHeight: baseBtcHeaderHeight, + BaseBtcHeaderHex: baseBtcHeaderHex, + AllowedReporterAddresses: allowedReporterAddresses, + CovenantPKs: strings.Split(covenantPks, ","), + CovenantQuorum: covenantQuorum, + SlashingAddress: slashingAddress, + MinSlashingTransactionFeeSat: minSlashingFee, + MinCommissionRate: math.LegacyMustNewDecFromStr(minCommissionRate), + SlashingRate: math.LegacyMustNewDecFromStr(slashingRate), + MaxActiveFinalityProviders: maxActiveFinalityProviders, + MinUnbondingTime: minUnbondingTime, + MinPubRand: minPubRand, + GenesisTime: genesisTime, + InflationRateChange: inflationRateChange, + InflationMax: inflationMax, + InflationMin: inflationMin, + GoalBonded: goalBonded, + BlocksPerYear: blocksPerYear, + BlockGasLimit: blockGasLimit, + VoteExtensionEnableHeight: voteExtensionEnableHeight, } } diff --git a/cmd/babylond/cmd/genaccounts.go b/cmd/babylond/cmd/genaccounts.go index 65d9d547f..b30395cbb 100644 --- a/cmd/babylond/cmd/genaccounts.go +++ b/cmd/babylond/cmd/genaccounts.go @@ -90,7 +90,10 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) if !vestingAmt.IsZero() { - baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) + baseVestingAccount, err := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) + if err != nil { + return err + } if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { diff --git a/cmd/babylond/cmd/genaccounts_test.go b/cmd/babylond/cmd/genaccounts_test.go index 3f18a76be..0142009a1 100644 --- a/cmd/babylond/cmd/genaccounts_test.go +++ b/cmd/babylond/cmd/genaccounts_test.go @@ -3,13 +3,14 @@ package cmd_test import ( "context" "fmt" + "github.com/babylonchain/babylon/app" "testing" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cometbft/cometbft/libs/log" + "cosmossdk.io/log" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -21,7 +22,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil" genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" - "github.com/babylonchain/babylon/app" bbncmd "github.com/babylonchain/babylon/cmd/babylond/cmd" ) @@ -66,15 +66,15 @@ func TestAddGenesisAccountCmd(t *testing.T) { }, } + appCodec := app.GetEncodingConfig().Codec for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { home := t.TempDir() logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + cfg, err := genutiltest.CreateDefaultCometConfig(home) require.NoError(t, err) - appCodec := app.GetEncodingConfig().Marshaler err = genutiltest.ExecInitCmd(testMbm, home, appCodec) require.NoError(t, err) diff --git a/cmd/babylond/cmd/genbls.go b/cmd/babylond/cmd/genbls.go index 5534cd60b..b667178b8 100644 --- a/cmd/babylond/cmd/genbls.go +++ b/cmd/babylond/cmd/genbls.go @@ -5,12 +5,13 @@ import ( "path/filepath" "strings" - "github.com/babylonchain/babylon/app" - "github.com/babylonchain/babylon/privval" - tmconfig "github.com/cometbft/cometbft/config" - tmos "github.com/cometbft/cometbft/libs/os" + cmtconfig "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/privval" ) func GenBlsCmd() *cobra.Command { @@ -32,10 +33,10 @@ $ babylond genbls --home ./ RunE: func(cmd *cobra.Command, args []string) error { homeDir, _ := cmd.Flags().GetString(flags.FlagHome) - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - if !tmos.FileExists(keyPath) { + if !cmtos.FileExists(keyPath) { return errors.New("validator key file does not exist") } diff --git a/cmd/babylond/cmd/genbls_test.go b/cmd/babylond/cmd/genbls_test.go index bf9abc7c4..b79486fb1 100644 --- a/cmd/babylond/cmd/genbls_test.go +++ b/cmd/babylond/cmd/genbls_test.go @@ -7,8 +7,10 @@ import ( "path/filepath" "testing" - tmconfig "github.com/cometbft/cometbft/config" - "github.com/cometbft/cometbft/libs/log" + dbm "github.com/cosmos/cosmos-db" + + "cosmossdk.io/log" + cmtconfig "github.com/cometbft/cometbft/config" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -28,19 +30,27 @@ import ( func Test_GenBlsCmd(t *testing.T) { home := t.TempDir() - encodingConfig := app.GetEncodingConfig() logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + cfg, err := genutiltest.CreateDefaultCometConfig(home) require.NoError(t, err) - err = genutiltest.ExecInitCmd(app.ModuleBasics, home, encodingConfig.Marshaler) + signer, err := app.SetupTestPrivSigner() + require.NoError(t, err) + bbn := app.NewBabylonAppWithCustomOptions(t, false, signer, app.SetupOptions{ + Logger: logger, + DB: dbm.NewMemDB(), + InvCheckPeriod: 0, + SkipUpgradeHeights: map[int64]bool{}, + AppOpts: app.EmptyAppOptions{}, + }) + err = genutiltest.ExecInitCmd(bbn.BasicModuleManager, home, bbn.AppCodec()) require.NoError(t, err) serverCtx := server.NewContext(viper.New(), cfg, logger) clientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). + WithCodec(bbn.AppCodec()). WithHomeDir(home). - WithTxConfig(encodingConfig.TxConfig) + WithTxConfig(bbn.TxConfig()) ctx := context.Background() ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) @@ -58,7 +68,7 @@ func Test_GenBlsCmd(t *testing.T) { require.NoError(t, err) // create BLS keys - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) filePV := privval.GenWrappedFilePV(keyPath, statePath) diff --git a/cmd/babylond/cmd/genesis.go b/cmd/babylond/cmd/genesis.go index fd61482d9..27d9d73ac 100644 --- a/cmd/babylond/cmd/genesis.go +++ b/cmd/babylond/cmd/genesis.go @@ -5,12 +5,11 @@ import ( "fmt" "time" - appparams "github.com/babylonchain/babylon/app/params" - bbn "github.com/babylonchain/babylon/types" - btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + sdkmath "cosmossdk.io/math" + + btcstakingtypes "github.com/babylonchain/babylon/x/btcstaking/types" + finalitytypes "github.com/babylonchain/babylon/x/finality/types" + comettypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -29,6 +28,13 @@ import ( minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/spf13/cobra" + + appparams "github.com/babylonchain/babylon/app/params" + bbn "github.com/babylonchain/babylon/types" + btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" ) func PrepareGenesisCmd(defaultNodeHome string, mbm module.BasicManager) *cobra.Command { @@ -52,7 +58,7 @@ Example: genFile := config.GenesisFile() - appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + genesisState, genesis, err := genutiltypes.GenesisStateFromGenFile(genFile) if err != nil { return fmt.Errorf("failed to unmarshal genesis state: %s", err) } @@ -65,9 +71,13 @@ Example: genesisParams = TestnetGenesisParams(genesisCliArgs.MaxActiveValidators, genesisCliArgs.BtcConfirmationDepth, genesisCliArgs.BtcFinalizationTimeout, genesisCliArgs.CheckpointTag, genesisCliArgs.EpochInterval, genesisCliArgs.BaseBtcHeaderHex, - genesisCliArgs.BaseBtcHeaderHeight, genesisCliArgs.InflationRateChange, + genesisCliArgs.BaseBtcHeaderHeight, genesisCliArgs.AllowedReporterAddresses, + genesisCliArgs.CovenantPKs, genesisCliArgs.CovenantQuorum, + genesisCliArgs.SlashingAddress, genesisCliArgs.MinSlashingTransactionFeeSat, + genesisCliArgs.MinCommissionRate, genesisCliArgs.SlashingRate, genesisCliArgs.MaxActiveFinalityProviders, + genesisCliArgs.MinUnbondingTime, genesisCliArgs.MinPubRand, genesisCliArgs.InflationRateChange, genesisCliArgs.InflationMin, genesisCliArgs.InflationMax, genesisCliArgs.GoalBonded, - genesisCliArgs.BlocksPerYear, genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit) + genesisCliArgs.BlocksPerYear, genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit, genesisCliArgs.VoteExtensionEnableHeight) } else if network == "mainnet" { // TODO: mainnet genesis params panic("Mainnet params not implemented.") @@ -75,16 +85,16 @@ Example: return fmt.Errorf("please choose testnet or mainnet") } - err = PrepareGenesis(clientCtx, appState, genDoc, genesisParams, chainID) + err = PrepareGenesis(clientCtx, genesisState, genesis, genesisParams, chainID) if err != nil { return fmt.Errorf("failed to prepare genesis: %w", err) } - if err = mbm.ValidateGenesis(clientCtx.Codec, clientCtx.TxConfig, appState); err != nil { + if err = mbm.ValidateGenesis(clientCtx.Codec, clientCtx.TxConfig, genesisState); err != nil { return fmt.Errorf("error validating genesis file: %s", err) } - return genutil.ExportGenesisFile(genDoc, genFile) + return genutil.ExportGenesisFile(genesis, genFile) }, } @@ -96,12 +106,12 @@ Example: func PrepareGenesis( clientCtx client.Context, - appState map[string]json.RawMessage, - genDoc *comettypes.GenesisDoc, + genesisState map[string]json.RawMessage, + genesis *genutiltypes.AppGenesis, genesisParams GenesisParams, chainID string, ) error { - if genDoc == nil { + if genesis == nil { return fmt.Errorf("provided genesis must not be nil") } @@ -109,66 +119,79 @@ func PrepareGenesis( cdc := depCdc // Add ChainID - genDoc.ChainID = chainID - genDoc.GenesisTime = genesisParams.GenesisTime + genesis.ChainID = chainID + genesis.GenesisTime = genesisParams.GenesisTime + + if genesis.Consensus == nil { + genesis.Consensus = genutiltypes.NewConsensusGenesis(comettypes.DefaultConsensusParams().ToProto(), nil) - if genDoc.ConsensusParams == nil { - genDoc.ConsensusParams = comettypes.DefaultConsensusParams() } // Set gas limit - genDoc.ConsensusParams.Block.MaxGas = genesisParams.BlockGasLimit + genesis.Consensus.Params.Block.MaxGas = genesisParams.BlockGasLimit + genesis.Consensus.Params.ABCI.VoteExtensionsEnableHeight = genesisParams.VoteExtensionsEnableHeight // Set the confirmation and finalization parameters btccheckpointGenState := btccheckpointtypes.DefaultGenesis() btccheckpointGenState.Params = genesisParams.BtccheckpointParams - appState[btccheckpointtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(btccheckpointGenState) + genesisState[btccheckpointtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(btccheckpointGenState) // btclightclient genesis btclightclientGenState := btclightclienttypes.DefaultGenesis() btclightclientGenState.BaseBtcHeader = genesisParams.BtclightclientBaseBtcHeader - appState[btclightclienttypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(btclightclientGenState) + btclightclientGenState.Params = genesisParams.BtclightclientParams + genesisState[btclightclienttypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(btclightclientGenState) // epoching module genesis epochingGenState := epochingtypes.DefaultGenesis() epochingGenState.Params = genesisParams.EpochingParams - appState[epochingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(epochingGenState) + genesisState[epochingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(epochingGenState) // checkpointing module genesis checkpointingGenState := checkpointingtypes.DefaultGenesis() checkpointingGenState.GenesisKeys = genesisParams.CheckpointingGenKeys - appState[checkpointingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(checkpointingGenState) + genesisState[checkpointingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(checkpointingGenState) + + // btcstaking module genesis + btcstakingGenState := btcstakingtypes.DefaultGenesis() + btcstakingGenState.Params = genesisParams.BtcstakingParams + genesisState[btcstakingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(btcstakingGenState) + + // finality module genesis + finalityGenState := finalitytypes.DefaultGenesis() + finalityGenState.Params = genesisParams.FinalityParams + genesisState[finalitytypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(finalityGenState) // staking module genesis - stakingGenState := stakingtypes.GetGenesisStateFromAppState(depCdc, appState) - clientCtx.Codec.MustUnmarshalJSON(appState[stakingtypes.ModuleName], stakingGenState) + stakingGenState := stakingtypes.GetGenesisStateFromAppState(depCdc, genesisState) + clientCtx.Codec.MustUnmarshalJSON(genesisState[stakingtypes.ModuleName], stakingGenState) stakingGenState.Params = genesisParams.StakingParams - appState[stakingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(stakingGenState) + genesisState[stakingtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(stakingGenState) // mint module genesis mintGenState := minttypes.DefaultGenesisState() mintGenState.Params = genesisParams.MintParams - appState[minttypes.ModuleName] = cdc.MustMarshalJSON(mintGenState) + genesisState[minttypes.ModuleName] = cdc.MustMarshalJSON(mintGenState) // distribution module genesis distributionGenState := distributiontypes.DefaultGenesisState() distributionGenState.Params = genesisParams.DistributionParams - appState[distributiontypes.ModuleName] = cdc.MustMarshalJSON(distributionGenState) + genesisState[distributiontypes.ModuleName] = cdc.MustMarshalJSON(distributionGenState) // gov module genesis govGenState := govv1.DefaultGenesisState() govGenState.Params = &genesisParams.GovParams - appState[govtypes.ModuleName] = cdc.MustMarshalJSON(govGenState) + genesisState[govtypes.ModuleName] = cdc.MustMarshalJSON(govGenState) // crisis module genesis crisisGenState := crisistypes.DefaultGenesisState() crisisGenState.ConstantFee = genesisParams.CrisisConstantFee - appState[crisistypes.ModuleName] = cdc.MustMarshalJSON(crisisGenState) + genesisState[crisistypes.ModuleName] = cdc.MustMarshalJSON(crisisGenState) // auth module genesis authGenState := authtypes.DefaultGenesisState() authGenState.Accounts = genesisParams.AuthAccounts - appState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState) + genesisState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState) // bank module genesis bankGenState := banktypes.DefaultGenesisState() @@ -176,15 +199,15 @@ func PrepareGenesis( for _, bal := range bankGenState.Balances { bankGenState.Supply = bankGenState.Supply.Add(bal.Coins...) } - appState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState) + genesisState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState) - appGenStateJSON, err := json.MarshalIndent(appState, "", " ") + appGenStateJSON, err := json.MarshalIndent(genesisState, "", " ") if err != nil { return err } - genDoc.AppState = appGenStateJSON + genesis.AppState = appGenStateJSON return nil } @@ -206,15 +229,21 @@ type GenesisParams struct { BtccheckpointParams btccheckpointtypes.Params EpochingParams epochingtypes.Params + BtcstakingParams btcstakingtypes.Params + FinalityParams finalitytypes.Params BtclightclientBaseBtcHeader btclightclienttypes.BTCHeaderInfo + BtclightclientParams btclightclienttypes.Params BlockGasLimit int64 + VoteExtensionsEnableHeight int64 } func TestnetGenesisParams(maxActiveValidators uint32, btcConfirmationDepth uint64, btcFinalizationTimeout uint64, checkpointTag string, epochInterval uint64, baseBtcHeaderHex string, - baseBtcHeaderHeight uint64, inflationRateChange float64, + baseBtcHeaderHeight uint64, allowedReporters []string, covenantPKs []string, covenantQuorum uint32, slashingAddress string, minSlashingFee int64, + minCommissionRate sdkmath.LegacyDec, slashingRate sdkmath.LegacyDec, maxActiveFinalityProviders uint32, minUnbondingTime uint16, + minPubRand uint64, inflationRateChange float64, inflationMin float64, inflationMax float64, goalBonded float64, - blocksPerYear uint64, genesisTime time.Time, blockGasLimit int64) GenesisParams { + blocksPerYear uint64, genesisTime time.Time, blockGasLimit int64, voteExtensionEnableHeight int64) GenesisParams { genParams := GenesisParams{} @@ -251,20 +280,20 @@ func TestnetGenesisParams(maxActiveValidators uint32, btcConfirmationDepth uint6 genParams.MintParams.MintDenom = genParams.NativeCoinMetadatas[0].Base genParams.MintParams.BlocksPerYear = blocksPerYear // This should always work as inflation rate is already a float64 - genParams.MintParams.InflationRateChange = sdk.MustNewDecFromStr(fmt.Sprintf("%f", inflationRateChange)) - genParams.MintParams.InflationMin = sdk.MustNewDecFromStr(fmt.Sprintf("%f", inflationMin)) - genParams.MintParams.InflationMax = sdk.MustNewDecFromStr(fmt.Sprintf("%f", inflationMax)) - genParams.MintParams.GoalBonded = sdk.MustNewDecFromStr(fmt.Sprintf("%f", goalBonded)) + genParams.MintParams.InflationRateChange = sdkmath.LegacyMustNewDecFromStr(fmt.Sprintf("%f", inflationRateChange)) + genParams.MintParams.InflationMin = sdkmath.LegacyMustNewDecFromStr(fmt.Sprintf("%f", inflationMin)) + genParams.MintParams.InflationMax = sdkmath.LegacyMustNewDecFromStr(fmt.Sprintf("%f", inflationMax)) + genParams.MintParams.GoalBonded = sdkmath.LegacyMustNewDecFromStr(fmt.Sprintf("%f", goalBonded)) genParams.GovParams = govv1.DefaultParams() genParams.GovParams.MinDeposit = sdk.NewCoins(sdk.NewCoin( genParams.NativeCoinMetadatas[0].Base, - sdk.NewInt(2_500_000_000), + sdkmath.NewInt(2_500_000_000), )) genParams.CrisisConstantFee = sdk.NewCoin( genParams.NativeCoinMetadatas[0].Base, - sdk.NewInt(500_000_000_000), + sdkmath.NewInt(500_000_000_000), ) genParams.BtccheckpointParams = btccheckpointtypes.DefaultParams() @@ -283,13 +312,53 @@ func TestnetGenesisParams(maxActiveValidators uint32, btcConfirmationDepth uint6 } work := btclightclienttypes.CalcWork(&baseBtcHeader) baseBtcHeaderInfo := btclightclienttypes.NewBTCHeaderInfo(&baseBtcHeader, baseBtcHeader.Hash(), baseBtcHeaderHeight, &work) + + params, err := btclightclienttypes.NewParamsValidate(allowedReporters) + + if err != nil { + panic(err) + } + genParams.BtclightclientBaseBtcHeader = *baseBtcHeaderInfo + genParams.BtclightclientParams = params + + genParams.BtcstakingParams = btcstakingtypes.DefaultParams() + covenantPKsBIP340 := make([]bbn.BIP340PubKey, 0, len(covenantPKs)) + for _, pkHex := range covenantPKs { + pk, err := bbn.NewBIP340PubKeyFromHex(pkHex) + if err != nil { + panic(err) + } + covenantPKsBIP340 = append(covenantPKsBIP340, *pk) + } + genParams.BtcstakingParams.CovenantPks = covenantPKsBIP340 + genParams.BtcstakingParams.CovenantQuorum = covenantQuorum + genParams.BtcstakingParams.SlashingAddress = slashingAddress + genParams.BtcstakingParams.MinSlashingTxFeeSat = minSlashingFee + genParams.BtcstakingParams.MinCommissionRate = minCommissionRate + genParams.BtcstakingParams.SlashingRate = slashingRate + genParams.BtcstakingParams.MaxActiveFinalityProviders = maxActiveFinalityProviders + genParams.BtcstakingParams.MinUnbondingTime = uint32(minUnbondingTime) + if err := genParams.BtcstakingParams.Validate(); err != nil { + panic(err) + } + + genParams.FinalityParams = finalitytypes.DefaultParams() + genParams.FinalityParams.MinPubRand = minPubRand + if err := genParams.FinalityParams.Validate(); err != nil { + panic(err) + } if epochInterval == 0 { panic(fmt.Sprintf("Invalid epoch interval %d", epochInterval)) } genParams.EpochingParams = epochingtypes.DefaultParams() genParams.EpochingParams.EpochInterval = epochInterval + if err := genParams.EpochingParams.Validate(); err != nil { + panic(err) + } + genParams.BlockGasLimit = blockGasLimit + genParams.VoteExtensionsEnableHeight = voteExtensionEnableHeight return genParams } diff --git a/cmd/babylond/cmd/root.go b/cmd/babylond/cmd/root.go index 709402c37..94025ee60 100644 --- a/cmd/babylond/cmd/root.go +++ b/cmd/babylond/cmd/root.go @@ -4,20 +4,24 @@ import ( "errors" "io" "os" - "path/filepath" - rosettaCmd "cosmossdk.io/tools/rosetta/cmd" + confixcmd "cosmossdk.io/tools/confix/cmd" "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - dbm "github.com/cometbft/cometbft-db" - tmcfg "github.com/cometbft/cometbft/config" - tmcli "github.com/cometbft/cometbft/libs/cli" - tmtypes "github.com/cometbft/cometbft/types" + cmtcfg "github.com/cometbft/cometbft/config" + cmtcli "github.com/cometbft/cometbft/libs/cli" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/baseapp" + "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/client/debug" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/keys" @@ -25,35 +29,32 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" - "github.com/cosmos/cosmos-sdk/snapshots" - snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" - "github.com/cosmos/cosmos-sdk/x/genutil" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/app/params" - bbntypes "github.com/babylonchain/babylon/types" ) // NewRootCmd creates a new root command for babylond. It is called once in the // main function. -func NewRootCmd() (*cobra.Command, params.EncodingConfig) { - encodingConfig := app.GetEncodingConfig() +func NewRootCmd() *cobra.Command { + // we "pre"-instantiate the application for getting the injected/configured encoding configuration + // note, this is not necessary when using app wiring, as depinject can be directly used + tempApp := app.NewTmpBabylonApp() + initClientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithLegacyAmino(encodingConfig.Amino). + WithCodec(tempApp.AppCodec()). + WithInterfaceRegistry(tempApp.InterfaceRegistry()). + WithTxConfig(tempApp.TxConfig()). + WithLegacyAmino(tempApp.LegacyAmino()). WithInput(os.Stdin). WithAccountRetriever(types.AccountRetriever{}). WithHomeDir(app.DefaultNodeHome). @@ -67,6 +68,7 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.ErrOrStderr()) + initClientCtx = initClientCtx.WithCmdContext(cmd.Context()) initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) if err != nil { return err @@ -77,14 +79,31 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { return err } + if !initClientCtx.Offline { + enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) + txConfigOpts := tx.ConfigOptions{ + EnabledSignModes: enabledSignModes, + TextualCoinMetadataQueryFn: authtxconfig.NewGRPCCoinMetadataQueryFn(initClientCtx), + } + txConfig, err := tx.NewTxConfigWithOptions( + initClientCtx.Codec, + txConfigOpts, + ) + if err != nil { + return err + } + + initClientCtx = initClientCtx.WithTxConfig(txConfig) + } + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { return err } customAppTemplate, customAppConfig := initAppConfig() - customTMConfig := initTendermintConfig() + customCometConfig := initCometConfig() - err = server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) + err = server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customCometConfig) if err != nil { return err @@ -94,15 +113,29 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { }, } - initRootCmd(rootCmd, encodingConfig) + initRootCmd(rootCmd, tempApp.TxConfig(), tempApp.BasicModuleManager) + + // add keyring to autocli opts + autoCliOpts := tempApp.AutoCliOpts() + initClientCtx, _ = config.ReadFromClientConfig(initClientCtx) + autoCliOpts.Keyring, _ = keyring.NewAutoCLIKeyring(initClientCtx.Keyring) + autoCliOpts.ClientCtx = initClientCtx + autoCliOpts.TxConfigOpts = tx.ConfigOptions{ + EnabledSignModes: tx.DefaultSignModes, + TextualCoinMetadataQueryFn: authtxconfig.NewGRPCCoinMetadataQueryFn(initClientCtx), + } - return rootCmd, encodingConfig + if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { + panic(err) + } + + return rootCmd } -// initTendermintConfig helps to override default Tendermint Config values. +// initCometConfig helps to override default Comet Config values. // return tmcfg.DefaultConfig if no custom configuration is required for the application. -func initTendermintConfig() *tmcfg.Config { - cfg := tmcfg.DefaultConfig() +func initCometConfig() *cmtcfg.Config { + cfg := cmtcfg.DefaultConfig() // these values put a higher strain on node memory // cfg.P2P.MaxNumInboundPeers = 100 @@ -132,47 +165,43 @@ func initAppConfig() (string, interface{}) { // own app.toml to override, or use this default value. // // In app, we set the min gas prices to 0. - babylonConfig.MinGasPrices = "0bbn" + babylonConfig.MinGasPrices = "0ubbn" return babylonTemplate, babylonConfig } -func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { +func initRootCmd(rootCmd *cobra.Command, txConfig client.TxEncodingConfig, basicManager module.BasicManager) { cfg := sdk.GetConfig() cfg.Seal() - gentxModule := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) + gentxModule := basicManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) rootCmd.AddCommand( - genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome), - genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, gentxModule.GenTxValidator), - genutilcli.MigrateGenesisCmd(), - genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), - ValidateGenesisCmd(app.ModuleBasics, gentxModule.GenTxValidator), - PrepareGenesisCmd(app.DefaultNodeHome, app.ModuleBasics), + genutilcli.InitCmd(basicManager, app.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, gentxModule.GenTxValidator, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), + genutilcli.MigrateGenesisCmd(genutilcli.MigrationMap), + genutilcli.GenTxCmd(basicManager, txConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), + ValidateGenesisCmd(basicManager, gentxModule.GenTxValidator), + PrepareGenesisCmd(app.DefaultNodeHome, basicManager), AddGenesisAccountCmd(app.DefaultNodeHome), - tmcli.NewCompletionCmd(rootCmd, true), - TestnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}), + cmtcli.NewCompletionCmd(rootCmd, true), + TestnetCmd(basicManager, banktypes.GenesisBalancesIterator{}), CreateBlsKeyCmd(), GenBlsCmd(), AddGenBlsCmd(gentxModule.GenTxValidator), debug.Cmd(), - config.Cmd(), + confixcmd.ConfigCommand(), ) - a := appCreator{encodingConfig} - server.AddCommands(rootCmd, app.DefaultNodeHome, a.newApp, a.appExport, addModuleInitFlags) + server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, addModuleInitFlags) // add keybase, auxiliary RPC, query, and tx child commands rootCmd.AddCommand( - rpc.StatusCommand(), + server.StatusCommand(), queryCommand(), txCommand(), - keys.Commands(app.DefaultNodeHome), + keys.Commands(), ) - - // add rosetta - rootCmd.AddCommand(rosettaCmd.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) } func addModuleInitFlags(startCmd *cobra.Command) { @@ -194,14 +223,15 @@ func queryCommand() *cobra.Command { } cmd.AddCommand( - authcmd.GetAccountCmd(), + rpc.QueryEventForTxCmd(), rpc.ValidatorCommand(), - rpc.BlockCommand(), + server.QueryBlockCmd(), authcmd.QueryTxsByEventsCmd(), + server.QueryBlocksCmd(), authcmd.QueryTxCmd(), + server.QueryBlockResultsCmd(), ) - app.ModuleBasics.AddQueryCommands(cmd) cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") return cmd @@ -225,120 +255,47 @@ func txCommand() *cobra.Command { authcmd.GetBroadcastCommand(), authcmd.GetEncodeCommand(), authcmd.GetDecodeCommand(), + authcmd.GetSimulateCmd(), ) - app.ModuleBasics.AddTxCommands(cmd) cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") return cmd } -type appCreator struct { - encCfg params.EncodingConfig -} - // newApp is an appCreator -func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { - var cache sdk.MultiStorePersistentCache - - // TODO migrate following option to using server.DefaultBaseappOptions - if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { - cache = store.NewCommitKVStoreCacheManager() - } +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { + baseappOptions := server.DefaultBaseappOptions(appOpts) skipUpgradeHeights := make(map[int64]bool) for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { skipUpgradeHeights[int64(h)] = true } - pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) - if err != nil { - panic(err) - } - homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) - - chainID := cast.ToString(appOpts.Get(flags.FlagChainID)) - if chainID == "" { - // fallback to genesis chain-id - appGenesis, err := tmtypes.GenesisDocFromFile(filepath.Join(homeDir, "config", "genesis.json")) - if err != nil { - panic(err) - } - chainID = appGenesis.ChainID - } - - snapshotDir := filepath.Join(homeDir, "data", "snapshots") - snapshotDB, err := dbm.NewDB("metadata", server.GetAppDBBackend(appOpts), snapshotDir) - - if err != nil { - panic(err) - } - snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) + privSigner, err := app.InitPrivSigner(homeDir) if err != nil { panic(err) } - clientCtx, err := config.ReadFromClientConfig( - client.Context{}. - WithHomeDir(homeDir). - WithViper(""). - WithKeyringDir(homeDir). - WithInput(os.Stdin). - // Warning: It is important that ReadFromClientConfig receives context - // with already initialized codec. It creates keyring inside, and from cosmos - // 0.46.0 keyring requires codec. Without codec, operations performed by - // keyring ends with nil pointer exception (`panic`) - WithCodec(a.encCfg.Marshaler), - ) - if err != nil { - panic(err) - } - // parse the key name that will be used for signing BLS-sig txs from app.toml - keyName := bbntypes.ParseKeyNameFromConfig(appOpts) - - privSigner, err := app.InitPrivSigner(clientCtx, homeDir, clientCtx.Keyring, keyName, a.encCfg) - if err != nil { - panic(err) - } - - snapshotOptions := snapshottypes.NewSnapshotOptions( - cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), - cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)), - ) - - var wasmOpts []wasm.Option + var wasmOpts []wasmkeeper.Option if cast.ToBool(appOpts.Get("telemetry.enabled")) { wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer)) } return app.NewBabylonApp( logger, db, traceStore, true, skipUpgradeHeights, - cast.ToString(appOpts.Get(flags.FlagHome)), cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), - a.encCfg, privSigner, appOpts, - app.GetWasmEnabledProposals(), wasmOpts, - baseapp.SetPruning(pruningOpts), - baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), - baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), - baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), - baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), - baseapp.SetInterBlockCache(cache), - baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), - baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), - baseapp.SetSnapshot(snapshotStore, snapshotOptions), - baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(server.FlagIAVLCacheSize))), - baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(server.FlagDisableIAVLFastNode))), - baseapp.SetChainID(chainID), + baseappOptions..., ) } // appExport creates a new app (optionally at a given height) // and exports state. -func (a appCreator) appExport( +func appExport( logger log.Logger, db dbm.DB, traceStore io.Writer, @@ -355,38 +312,18 @@ func (a appCreator) appExport( return servertypes.ExportedApp{}, errors.New("application home not set") } - clientCtx, err := config.ReadFromClientConfig( - client.Context{}. - WithHomeDir(homePath). - WithViper(""). - WithKeyringDir(homePath). - WithInput(os.Stdin). - // Warning: It is important that ReadFromClientConfig receives context - // with already initialized codec. It creates keyring inside, and from cosmos - // 0.46.0 keyring requires codec. Without codec, operations performed by - // keyring ends with nil pointer exception (`panic`) - WithCodec(a.encCfg.Marshaler), - ) - if err != nil { - panic(err) - } - kr, err := client.NewKeyringFromBackend(clientCtx, keyring.BackendMemory) - if err != nil { - panic(err) - } - - privSigner, err := app.InitPrivSigner(clientCtx, homePath, kr, "", a.encCfg) + privSigner, err := app.InitPrivSigner(homePath) if err != nil { panic(err) } if height != -1 { - babylonApp = app.NewBabylonApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, privSigner, appOpts, app.GetWasmEnabledProposals(), app.EmptyWasmOpts) + babylonApp = app.NewBabylonApp(logger, db, traceStore, false, map[int64]bool{}, uint(1), privSigner, appOpts, app.EmptyWasmOpts) if err = babylonApp.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { - babylonApp = app.NewBabylonApp(logger, db, traceStore, true, map[int64]bool{}, homePath, uint(1), a.encCfg, privSigner, appOpts, app.GetWasmEnabledProposals(), app.EmptyWasmOpts) + babylonApp = app.NewBabylonApp(logger, db, traceStore, true, map[int64]bool{}, uint(1), privSigner, appOpts, app.EmptyWasmOpts) } return babylonApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) diff --git a/cmd/babylond/cmd/testnet.go b/cmd/babylond/cmd/testnet.go index 6f3ac6cc1..6a9e8e348 100644 --- a/cmd/babylond/cmd/testnet.go +++ b/cmd/babylond/cmd/testnet.go @@ -1,7 +1,5 @@ package cmd -// DONTCOVER - import ( "bufio" "encoding/json" @@ -12,38 +10,37 @@ import ( "path/filepath" "time" - appparams "github.com/babylonchain/babylon/app/params" - bbn "github.com/babylonchain/babylon/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - - "github.com/babylonchain/babylon/app" - "github.com/cosmos/cosmos-sdk/crypto/hd" - - "github.com/babylonchain/babylon/privval" - "github.com/babylonchain/babylon/testutil/datagen" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - - tmconfig "github.com/cometbft/cometbft/config" - tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cometbft/cometbft/types" - tmtime "github.com/cometbft/cometbft/types/time" - "github.com/spf13/cobra" - + "cosmossdk.io/math" + cmtconfig "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" + cmttime "github.com/cometbft/cometbft/types/time" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/server" srvconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/app" + appparams "github.com/babylonchain/babylon/app/params" + "github.com/babylonchain/babylon/privval" + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" ) var ( @@ -97,15 +94,19 @@ Example: genesisParams := TestnetGenesisParams(genesisCliArgs.MaxActiveValidators, genesisCliArgs.BtcConfirmationDepth, genesisCliArgs.BtcFinalizationTimeout, genesisCliArgs.CheckpointTag, - genesisCliArgs.EpochInterval, genesisCliArgs.BaseBtcHeaderHex, - genesisCliArgs.BaseBtcHeaderHeight, genesisCliArgs.InflationRateChange, - genesisCliArgs.InflationMin, genesisCliArgs.InflationMax, genesisCliArgs.GoalBonded, - genesisCliArgs.BlocksPerYear, genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit) + genesisCliArgs.EpochInterval, genesisCliArgs.BaseBtcHeaderHex, genesisCliArgs.BaseBtcHeaderHeight, + genesisCliArgs.AllowedReporterAddresses, genesisCliArgs.CovenantPKs, genesisCliArgs.CovenantQuorum, + genesisCliArgs.SlashingAddress, genesisCliArgs.MinSlashingTransactionFeeSat, genesisCliArgs.MinCommissionRate, + genesisCliArgs.SlashingRate, genesisCliArgs.MaxActiveFinalityProviders, genesisCliArgs.MinUnbondingTime, + genesisCliArgs.MinPubRand, genesisCliArgs.InflationRateChange, genesisCliArgs.InflationMin, + genesisCliArgs.InflationMax, genesisCliArgs.GoalBonded, genesisCliArgs.BlocksPerYear, + genesisCliArgs.GenesisTime, genesisCliArgs.BlockGasLimit, genesisCliArgs.VoteExtensionEnableHeight) return InitTestnet( clientCtx, cmd, config, mbm, genBalIterator, outputDir, genesisCliArgs.ChainID, minGasPrices, nodeDirPrefix, nodeDaemonHome, startingIPAddress, keyringBackend, algo, numValidators, - btcNetwork, additionalAccount, timeBetweenBlocks, genesisParams, + btcNetwork, additionalAccount, timeBetweenBlocks, + clientCtx.TxConfig.SigningContext().ValidatorAddressCodec(), genesisParams, ) }, } @@ -128,11 +129,11 @@ Example: const nodeDirPerm = 0755 -// Initialize the testnet +// InitTestnet initialize the testnet func InitTestnet( clientCtx client.Context, cmd *cobra.Command, - nodeConfig *tmconfig.Config, + nodeConfig *cmtconfig.Config, mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, outputDir, @@ -147,6 +148,7 @@ func InitTestnet( btcNetwork string, additionalAccount bool, timeBetweenBlocks uint64, + valAddrCodec runtime.ValidatorAddressCodec, genesisParams GenesisParams, ) error { @@ -165,8 +167,8 @@ func InitTestnet( babylonConfig.BtcConfig.Network = btcNetwork // Explorer related config. Allow CORS connections. babylonConfig.API.EnableUnsafeCORS = true + babylonConfig.GRPC.Enable = true babylonConfig.GRPC.Address = "0.0.0.0:9090" - babylonConfig.GRPCWeb.Address = "0.0.0.0:9091" var ( genAccounts []authtypes.GenesisAccount @@ -224,7 +226,6 @@ func InitTestnet( _ = os.RemoveAll(outputDir) return err } - babylonConfig.SignerConfig.KeyName = nodeDirName // generate validator keys nodeIDs[i], valKeys[i], err = datagen.InitializeNodeValidatorFiles(nodeConfig, addr) @@ -259,7 +260,7 @@ func InitTestnet( genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) - valPubkey, err := cryptocodec.FromTmPubKeyInterface(valKeys[i].ValPubkey) + valPubkey, err := cryptocodec.FromCmtPubKeyInterface(valKeys[i].ValPubkey) if err != nil { return err } @@ -273,13 +274,17 @@ func InitTestnet( ValPubkey: valPubkey.(*ed25519.PubKey), } genKeys = append(genKeys, genKey) + valStr, err := valAddrCodec.BytesToString(sdk.ValAddress(addr)) + if err != nil { + return err + } createValMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), + valStr, valPubkey, sdk.NewCoin(genesisParams.NativeCoinMetadatas[0].Base, valTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), + stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()), + math.OneInt(), ) if err != nil { return err @@ -299,7 +304,7 @@ func InitTestnet( WithKeybase(kb). WithTxConfig(clientCtx.TxConfig) - if err = tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { + if err = tx.Sign(cmd.Context(), txFactory, nodeDirName, txBuilder, true); err != nil { return err } @@ -355,8 +360,8 @@ func InitTestnet( } coins := sdk.Coins{ - sdk.NewCoin("testtoken", sdk.NewInt(1000000000)), - sdk.NewCoin(genesisParams.NativeCoinMetadatas[0].Base, sdk.NewInt(1000000000000)), + sdk.NewCoin("testtoken", math.NewInt(1000000000)), + sdk.NewCoin(genesisParams.NativeCoinMetadatas[0].Base, math.NewInt(1000000000000)), } genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) @@ -403,20 +408,20 @@ func initGenFiles( // set the bls keys for the checkpointing module genesisParams.CheckpointingGenKeys = genKeys - genDoc := &types.GenesisDoc{} + genesis := &genutiltypes.AppGenesis{} - err = PrepareGenesis(clientCtx, appGenState, genDoc, genesisParams, chainID) + err = PrepareGenesis(clientCtx, appGenState, genesis, genesisParams, chainID) if err != nil { return err } // set initial validators to nil, they will be added by collectGenFiles based on // genesis tranascations - genDoc.Validators = nil + genesis.Consensus.Validators = nil // generate empty genesis files for each validator and save for i := 0; i < numValidators; i++ { - if err := genDoc.SaveAs(genFiles[i]); err != nil { + if err := genesis.SaveAs(genFiles[i]); err != nil { return err } } @@ -424,13 +429,13 @@ func initGenFiles( } func collectGenFiles( - clientCtx client.Context, nodeConfig *tmconfig.Config, chainID string, + clientCtx client.Context, nodeConfig *cmtconfig.Config, chainID string, nodeIDs []string, genKeys []*checkpointingtypes.GenesisKey, numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, ) error { var appState json.RawMessage - genTime := tmtime.Now() + genTime := cmttime.Now() for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) @@ -443,7 +448,7 @@ func collectGenFiles( nodeID, valPubKey := nodeIDs[i], genKeys[i].ValPubkey initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) - genDoc, err := types.GenesisDocFromFile(nodeConfig.GenesisFile()) + _, genesis, err := genutiltypes.GenesisStateFromGenFile(nodeConfig.GenesisFile()) if err != nil { return err } @@ -451,10 +456,12 @@ func collectGenFiles( nodeAppState, err := genutil.GenAppStateFromConfig( clientCtx.Codec, clientCtx.TxConfig, - nodeConfig, initCfg, - *genDoc, + nodeConfig, + initCfg, + genesis, genBalIterator, genutiltypes.DefaultMessageValidator, + authcodec.NewBech32Codec(appparams.Bech32PrefixValAddr), ) if err != nil { return err @@ -468,14 +475,18 @@ func collectGenFiles( // overwrite each validator's genesis file to have a canonical genesis time genFile := nodeConfig.GenesisFile() - newGendoc := types.GenesisDoc{ - GenesisTime: genTime, - AppState: appState, - ConsensusParams: genDoc.ConsensusParams, - ChainID: genDoc.ChainID, + newGenesis := genutiltypes.AppGenesis{ + AppName: genesis.AppName, + AppVersion: genesis.AppVersion, + GenesisTime: genTime, + ChainID: genesis.ChainID, + InitialHeight: genesis.InitialHeight, + AppHash: genesis.AppHash, + AppState: appState, + Consensus: genesis.Consensus, } - if err := genutil.ExportGenesisFile(&newGendoc, genFile); err != nil { + if err := genutil.ExportGenesisFile(&newGenesis, genFile); err != nil { return err } } @@ -511,12 +522,12 @@ func writeFile(name string, dir string, contents []byte) error { writePath := filepath.Join(dir) file := filepath.Join(writePath, name) - err := tmos.EnsureDir(writePath, 0755) + err := cmtos.EnsureDir(writePath, 0755) if err != nil { return err } - err = tmos.WriteFile(file, contents, 0644) + err = cmtos.WriteFile(file, contents, 0644) if err != nil { return err } diff --git a/cmd/babylond/cmd/testnet_test.go b/cmd/babylond/cmd/testnet_test.go index 82215080b..f529bdf7e 100644 --- a/cmd/babylond/cmd/testnet_test.go +++ b/cmd/babylond/cmd/testnet_test.go @@ -5,7 +5,9 @@ import ( "fmt" "testing" - "github.com/cometbft/cometbft/libs/log" + dbm "github.com/cosmos/cosmos-db" + + "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" @@ -20,24 +22,34 @@ import ( func Test_TestnetCmd(t *testing.T) { home := t.TempDir() - encodingConfig := app.GetEncodingConfig() logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + cfg, err := genutiltest.CreateDefaultCometConfig(home) require.NoError(t, err) - err = genutiltest.ExecInitCmd(app.ModuleBasics, home, encodingConfig.Marshaler) + signer, err := app.SetupTestPrivSigner() + require.NoError(t, err) + bbn := app.NewBabylonAppWithCustomOptions(t, false, signer, app.SetupOptions{ + Logger: logger, + DB: dbm.NewMemDB(), + InvCheckPeriod: 0, + SkipUpgradeHeights: map[int64]bool{}, + AppOpts: app.EmptyAppOptions{}, + }) + err = genutiltest.ExecInitCmd(bbn.BasicModuleManager, home, bbn.AppCodec()) require.NoError(t, err) serverCtx := server.NewContext(viper.New(), cfg, logger) clientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). - WithHomeDir(home). - WithTxConfig(encodingConfig.TxConfig) + WithCodec(bbn.AppCodec()). + WithInterfaceRegistry(bbn.InterfaceRegistry()). + WithLegacyAmino(bbn.LegacyAmino()). + WithTxConfig(bbn.TxConfig()). + WithHomeDir(home) ctx := context.Background() ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - cmd := TestnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}) + cmd := TestnetCmd(bbn.BasicModuleManager, banktypes.GenesisBalancesIterator{}) cmd.SetArgs([]string{fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), fmt.Sprintf("--output-dir=%s", home)}) err = cmd.ExecuteContext(ctx) require.NoError(t, err) @@ -46,6 +58,6 @@ func Test_TestnetCmd(t *testing.T) { appState, _, err := genutiltypes.GenesisStateFromGenFile(genFile) require.NoError(t, err) - bankGenState := banktypes.GetGenesisStateFromAppState(encodingConfig.Marshaler, appState) + bankGenState := banktypes.GetGenesisStateFromAppState(bbn.AppCodec(), appState) require.NotEmpty(t, bankGenState.Supply.String()) } diff --git a/cmd/babylond/cmd/validate_genesis.go b/cmd/babylond/cmd/validate_genesis.go index d17818bd3..61fba3f17 100644 --- a/cmd/babylond/cmd/validate_genesis.go +++ b/cmd/babylond/cmd/validate_genesis.go @@ -5,14 +5,13 @@ import ( "errors" "fmt" - "github.com/spf13/cobra" - - tmtypes "github.com/cometbft/cometbft/types" + cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/types/module" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/spf13/cobra" "github.com/babylonchain/babylon/x/checkpointing/types" ) @@ -71,8 +70,8 @@ func ValidateGenesisCmd(mbm module.BasicManager, validator genutiltypes.MessageV // validateGenDoc reads a genesis file and validates that it is a correct // Tendermint GenesisDoc. This function does not do any cosmos-related // validation. -func validateGenDoc(importGenesisFile string) (*tmtypes.GenesisDoc, error) { - genDoc, err := tmtypes.GenesisDocFromFile(importGenesisFile) +func validateGenDoc(importGenesisFile string) (*cmttypes.GenesisDoc, error) { + genDoc, err := cmttypes.GenesisDocFromFile(importGenesisFile) if err != nil { return nil, fmt.Errorf("%s. Make sure that"+ " you have correctly migrated all Tendermint consensus params, please see the"+ diff --git a/cmd/babylond/cmd/validate_genesis_test.go b/cmd/babylond/cmd/validate_genesis_test.go index 5406591d9..1d39caa13 100644 --- a/cmd/babylond/cmd/validate_genesis_test.go +++ b/cmd/babylond/cmd/validate_genesis_test.go @@ -6,79 +6,77 @@ import ( "fmt" "testing" + dbm "github.com/cosmos/cosmos-db" + + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/spf13/viper" "github.com/stretchr/testify/require" - "github.com/babylonchain/babylon/app" - "github.com/babylonchain/babylon/cmd/babylond/cmd" - "github.com/babylonchain/babylon/x/checkpointing/types" - - tmjson "github.com/cometbft/cometbft/libs/json" - "github.com/cometbft/cometbft/libs/log" - tmtypes "github.com/cometbft/cometbft/types" + "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/cmd/babylond/cmd" ) func TestCheckCorrespondence(t *testing.T) { homePath := t.TempDir() - encodingCft := app.GetEncodingConfig() - clientCtx := client.Context{}.WithCodec(encodingCft.Marshaler).WithTxConfig(encodingCft.TxConfig) - // generate valid genesis doc - validGenState, genDoc := generateTestGenesisState(homePath, 2) - validGenDocJSON, err := tmjson.MarshalIndent(genDoc, "", " ") - require.NoError(t, err) + bbn, appState := generateTestGenesisState(t, homePath, 2) + clientCtx := client.Context{}.WithCodec(bbn.AppCodec()).WithTxConfig(bbn.TxConfig()) - // generate mismatched genesis doc by deleting one item from gentx and genKeys in different positions - gentxs := genutiltypes.GetGenesisStateFromAppState(clientCtx.Codec, validGenState) - genKeys := types.GetGenesisStateFromAppState(clientCtx.Codec, validGenState) - gentxs.GenTxs = gentxs.GenTxs[:1] - genKeys.GenesisKeys = genKeys.GenesisKeys[1:] - genTxsBz, err := clientCtx.Codec.MarshalJSON(gentxs) + // Copy the appState into a new struct + bz, err := json.Marshal(appState) require.NoError(t, err) - genKeysBz, err := clientCtx.Codec.MarshalJSON(&genKeys) + var mismatchedAppState map[string]json.RawMessage + err = json.Unmarshal(bz, &mismatchedAppState) require.NoError(t, err) - validGenState[genutiltypes.ModuleName] = genTxsBz - validGenState[types.ModuleName] = genKeysBz - misMatchedGenStateBz, err := json.Marshal(validGenState) + + genutilGenesisState := genutiltypes.GetGenesisStateFromAppState(clientCtx.Codec, mismatchedAppState) + checkpointingGenesisState := checkpointingtypes.GetGenesisStateFromAppState(clientCtx.Codec, mismatchedAppState) + + // generate mismatched genesis doc by deleting one item from gentx and genKeys in different positions + genutilGenesisState.GenTxs = genutilGenesisState.GenTxs[:1] + checkpointingGenesisState.GenesisKeys = checkpointingGenesisState.GenesisKeys[1:] + + // Update the for the genutil module with the invalid data + genTxsBz, err := clientCtx.Codec.MarshalJSON(genutilGenesisState) require.NoError(t, err) - genDoc.AppState = misMatchedGenStateBz - misMatchedGenDocJSON, err := tmjson.MarshalIndent(genDoc, "", " ") + mismatchedAppState[genutiltypes.ModuleName] = genTxsBz + + // Update the for the checkpointing module with the invalid data + genKeysBz, err := clientCtx.Codec.MarshalJSON(&checkpointingGenesisState) require.NoError(t, err) + mismatchedAppState[checkpointingtypes.ModuleName] = genKeysBz testCases := []struct { - name string - genesis []byte - expErr bool + name string + appState map[string]json.RawMessage + expErr bool }{ { "valid genesis gentx and BLS key pair", - validGenDocJSON, + appState, false, }, { "mismatched genesis state", - misMatchedGenDocJSON, + mismatchedAppState, true, }, } - gentxModule := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) + gentxModule := bbn.BasicModuleManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) for _, tc := range testCases { - genDoc, err := tmtypes.GenesisDocFromJSON(tc.genesis) - require.NoError(t, err) - require.NotEmpty(t, genDoc) - genesisState, err := genutiltypes.GenesisStateFromGenDoc(*genDoc) - require.NoError(t, err) - require.NotEmpty(t, genesisState) - err = cmd.CheckCorrespondence(clientCtx, genesisState, gentxModule.GenTxValidator) + err = cmd.CheckCorrespondence(clientCtx, tc.appState, gentxModule.GenTxValidator) if tc.expErr { require.Error(t, err) } else { @@ -87,23 +85,32 @@ func TestCheckCorrespondence(t *testing.T) { } } -func generateTestGenesisState(home string, n int) (map[string]json.RawMessage, *tmtypes.GenesisDoc) { - encodingConfig := app.GetEncodingConfig() +func generateTestGenesisState(t *testing.T, home string, n int) (*app.BabylonApp, map[string]json.RawMessage) { logger := log.NewNopLogger() - cfg, _ := genutiltest.CreateDefaultTendermintConfig(home) + cfg, _ := genutiltest.CreateDefaultCometConfig(home) + + signer, err := app.SetupTestPrivSigner() + require.NoError(t, err) + bbn := app.NewBabylonAppWithCustomOptions(t, false, signer, app.SetupOptions{ + Logger: logger, + DB: dbm.NewMemDB(), + InvCheckPeriod: 0, + SkipUpgradeHeights: map[int64]bool{}, + AppOpts: app.EmptyAppOptions{}, + }) - _ = genutiltest.ExecInitCmd(app.ModuleBasics, home, encodingConfig.Marshaler) + _ = genutiltest.ExecInitCmd(bbn.BasicModuleManager, home, bbn.AppCodec()) serverCtx := server.NewContext(viper.New(), cfg, logger) clientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). + WithCodec(bbn.AppCodec()). WithHomeDir(home). - WithTxConfig(encodingConfig.TxConfig) + WithTxConfig(bbn.TxConfig()) ctx := context.Background() ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - testnetCmd := cmd.TestnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}) + testnetCmd := cmd.TestnetCmd(bbn.BasicModuleManager, banktypes.GenesisBalancesIterator{}) testnetCmd.SetArgs([]string{ fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), fmt.Sprintf("--v=%v", n), @@ -112,6 +119,6 @@ func generateTestGenesisState(home string, n int) (map[string]json.RawMessage, * _ = testnetCmd.ExecuteContext(ctx) genFile := cfg.GenesisFile() - appState, gendoc, _ := genutiltypes.GenesisStateFromGenFile(genFile) - return appState, gendoc + genState, _, _ := genutiltypes.GenesisStateFromGenFile(genFile) + return bbn, genState } diff --git a/cmd/babylond/main.go b/cmd/babylond/main.go index e86aed55f..338f0cb45 100644 --- a/cmd/babylond/main.go +++ b/cmd/babylond/main.go @@ -1,11 +1,11 @@ package main import ( + "cosmossdk.io/log" "os" "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/cmd/babylond/cmd" - "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" "github.com/babylonchain/babylon/app/params" @@ -13,15 +13,10 @@ import ( func main() { params.SetAddressPrefixes() - rootCmd, _ := cmd.NewRootCmd() + rootCmd := cmd.NewRootCmd() if err := svrcmd.Execute(rootCmd, app.BabylonAppEnvPrefix, app.DefaultNodeHome); err != nil { - switch e := err.(type) { - case server.ErrorCode: - os.Exit(e.Code) - - default: - os.Exit(1) - } + log.NewLogger(rootCmd.OutOrStderr()).Error("failure when running app", "err", err) + os.Exit(1) } } diff --git a/contrib/images/Makefile b/contrib/images/Makefile index 711077c7e..3b9d0818e 100644 --- a/contrib/images/Makefile +++ b/contrib/images/Makefile @@ -1,4 +1,6 @@ -all: babylond +RELAYER_TAG := $(shell grep '^ENV RELAYER_TAG' cosmos-relayer/Dockerfile | cut -f3 -d\ ) + +all: babylond cosmos-relayer babylond: babylond-rmi docker build --tag babylonchain/babylond -f babylond/Dockerfile \ @@ -7,4 +9,12 @@ babylond: babylond-rmi babylond-rmi: docker rmi babylonchain/babylond 2>/dev/null; true -.PHONY: all babylondbabylond-rmi +cosmos-relayer: cosmos-relayer-rmi + docker build --tag babylonchain/cosmos-relayer:${RELAYER_TAG} -f cosmos-relayer/Dockerfile \ + $(shell git rev-parse --show-toplevel)/contrib/images/cosmos-relayer + docker tag babylonchain/cosmos-relayer:${RELAYER_TAG} babylonchain/cosmos-relayer:latest + +cosmos-relayer-rmi: + docker rmi babylonchain/cosmos-relayer 2>/dev/null; true + +.PHONY: all babylond cosmos-relayer babylond-rmi cosmos-relayer-rmi diff --git a/contrib/images/babylond/Dockerfile b/contrib/images/babylond/Dockerfile index 71a148639..2e8add214 100644 --- a/contrib/images/babylond/Dockerfile +++ b/contrib/images/babylond/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20-alpine AS build-env +FROM golang:1.21 AS build-env # Customize to your build env @@ -7,15 +7,12 @@ ARG TARGETPLATFORM="linux/amd64" # Version to build. Default is empty ARG VERSION -# Use muslc for static libs -ARG BUILD_TAGS="muslc" ARG LEDGER_ENABLED="false" # Cosmos build options ARG COSMOS_BUILD_OPTIONS="" # Install cli tools for building and final image -RUN apk add --update --no-cache make git bash gcc linux-headers eudev-dev ncurses-dev openssh curl jq -RUN apk add --no-cache musl-dev +RUN apt-get update && apt-get install -y make git bash gcc curl jq # Build WORKDIR /go/src/github.com/babylonchain/babylon @@ -29,35 +26,35 @@ 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) && \ - wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/libwasmvm_muslc.$(uname -m).a \ - -O /lib/libwasmvm_muslc.a && \ - # verify checksum - wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/checksums.txt -O /tmp/checksums.txt && \ - sha256sum /lib/libwasmvm_muslc.a | grep $(cat /tmp/checksums.txt | grep libwasmvm_muslc.$(uname -m) | cut -d ' ' -f 1) - RUN LEDGER_ENABLED=$LEDGER_ENABLED \ BUILD_TAGS=$BUILD_TAGS \ COSMOS_BUILD_OPTIONS=$COSMOS_BUILD_OPTIONS \ - LINK_STATICALLY=true \ + LINK_STATICALLY=false \ make build -FROM alpine:3.14 AS run +FROM debian:bookworm-slim AS run # Create a user -RUN addgroup --gid 1137 -S babylon && adduser --uid 1137 -S babylon -G babylon -RUN apk add bash curl jq +RUN addgroup --gid 1137 --system babylon && adduser --uid 1137 --gid 1137 --system --home /home/babylon babylon +RUN apt-get update && apt-get install -y bash curl jq wget # Label should match your github repo LABEL org.opencontainers.image.source="https://github.com/babylonchain/babylond:${VERSION}" -# Install Libraries -# COPY --from=build-env /usr/lib/libgcc_s.so.1 /lib/ -# COPY --from=build-env /lib/ld-musl*.so.1* /lib +# Install libraries +# Cosmwasm - Download correct libwasmvm version +COPY --from=build-env /go/src/github.com/babylonchain/babylon/go.mod /tmp +RUN WASMVM_VERSION=$(grep github.com/CosmWasm/wasmvm /tmp/go.mod | cut -d' ' -f2) && \ + wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/libwasmvm.$(uname -m).so \ + -O /lib/libwasmvm.$(uname -m).so && \ + # verify checksum + wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/checksums.txt -O /tmp/checksums.txt && \ + sha256sum /lib/libwasmvm.$(uname -m).so | grep $(cat /tmp/checksums.txt | grep libwasmvm.$(uname -m) | cut -d ' ' -f 1) +RUN rm -f /tmp/go.mod COPY --from=build-env /go/src/github.com/babylonchain/babylon/build/babylond /bin/babylond # Set home directory and user WORKDIR /home/babylon RUN chown -R babylon /home/babylon +RUN chmod g+s /home/babylon USER babylon diff --git a/contrib/images/cosmos-relayer/Dockerfile b/contrib/images/cosmos-relayer/Dockerfile new file mode 100644 index 000000000..7f03954e5 --- /dev/null +++ b/contrib/images/cosmos-relayer/Dockerfile @@ -0,0 +1,40 @@ +FROM debian:bullseye-slim AS build-env + +RUN apt-get update && apt-get install -y git make jq gcc make wget + +WORKDIR /work + +ARG TARGETARCH + +# Download and install Go +ENV GOLANG_VERSION 1.21.4 +RUN echo "Target arch: ${TARGETARCH}" +RUN echo "Go version: ${GOLANG_VERSION}" +RUN wget -q https://golang.org/dl/go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz && \ + tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz && \ + rm go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz +# Set Go environment variables +ENV PATH /usr/local/go/bin:$PATH +ENV GOPATH /go +ENV PATH $GOPATH/bin:$PATH + +WORKDIR /work + +ENV GO111MODULE on +ENV RELAYER_TAG main +# ENV RELAYER_TAG v2.4.2 + +# Install the relayer +RUN git clone https://github.com/cosmos/relayer.git +RUN cd relayer && git fetch origin && git checkout ${RELAYER_TAG} && make install && cd - + +FROM debian:bullseye-slim AS run +# Create a user +RUN addgroup --system rly && adduser --system --gid 101 rly + +COPY --from=build-env /go/bin/rly /usr/bin/rly + +# Set home directory and user +WORKDIR /home/rly +RUN chown -R rly /home/rly +USER rly diff --git a/crypto/bip322/bip322.go b/crypto/bip322/bip322.go new file mode 100644 index 000000000..48b73a9fa --- /dev/null +++ b/crypto/bip322/bip322.go @@ -0,0 +1,290 @@ +package bip322 + +import ( + "crypto/sha256" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +const ( + bip322Tag = "BIP0322-signed-message" + + // toSpend tx constants + toSpendVersion = 0 + toSpendLockTime = 0 + toSpendInputHash = "0000000000000000000000000000000000000000000000000000000000000000" + toSpendInputIndex = 0xFFFFFFFF + toSpendInputSeq = 0 + toSpendOutputValue = 0 + + // toSign tx constants + toSignVersion = 0 + toSignLockTime = 0 + toSignInputSeq = 0 + toSignOutputValue = 0 +) + +// GetBIP340TaggedHash builds a BIP-340 tagged hash +// More specifically, the hash is of the form +// sha256(sha256(tag) || sha256(tag) || msg) +// See https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0340.mediawiki#design +// for more details +func GetBIP340TaggedHash(msg []byte) [32]byte { + tagHash := sha256.Sum256([]byte(bip322Tag)) + sum := make([]byte, 0) + sum = append(sum, tagHash[:]...) + sum = append(sum, tagHash[:]...) + sum = append(sum, msg...) + return sha256.Sum256(sum) +} + +// toSpendSignatureScript creates the signature script for the input +// of the toSpend transaction, i.e. +// `OP_0 PUSH32 [ BIP340_TAGGED_MSG ]` +// https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0322.mediawiki#full +func toSpendSignatureScript(msg []byte) ([]byte, error) { + builder := txscript.NewScriptBuilder() + builder.AddOp(txscript.OP_0) + data := GetBIP340TaggedHash(msg) + builder.AddData(data[:]) + script, err := builder.Script() + if err != nil { + // msg depends on the input, so play it safe here and don't panic + return nil, err + } + return script, nil +} + +// toSignPkScript creates the public key script for the output +// of the toSign transaction, i.e. +// `OP_RETURN` +// https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0322.mediawiki#full +func toSignPkScript() []byte { + builder := txscript.NewScriptBuilder() + builder.AddOp(txscript.OP_RETURN) + script, err := builder.Script() + if err != nil { + // Panic as we're building the script entirely ourselves + panic(err) + } + return script +} + +// GetToSpendTx builds a toSpend transaction based on the BIP-322 spec +// https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0322.mediawiki#full +// It requires as input the message that is signed and the address that produced the signature +func GetToSpendTx(msg []byte, address btcutil.Address) (*wire.MsgTx, error) { + toSpend := wire.NewMsgTx(toSpendVersion) + toSpend.LockTime = toSpendLockTime + + // Create a single input with dummy data based on the spec constants + inputHash, err := chainhash.NewHashFromStr(toSpendInputHash) + if err != nil { + // This is a constant we have defined, so an issue here is a programming error + panic(err) + } + outPoint := wire.NewOutPoint(inputHash, toSpendInputIndex) + + // The signature script containing the BIP-322 Tagged message + script, err := toSpendSignatureScript(msg) + if err != nil { + return nil, err + } + input := wire.NewTxIn(outPoint, script, nil) + input.Sequence = toSpendInputSeq + + // Create the output + // The PK Script should be a pay to addr script on the provided address + pkScript, err := txscript.PayToAddrScript(address) + + if err != nil { + return nil, err + } + + output := wire.NewTxOut(toSpendOutputValue, pkScript) + + toSpend.AddTxIn(input) + toSpend.AddTxOut(output) + return toSpend, nil +} + +// GetToSignTx builds a toSign transaction based on the BIP-322 spec +// https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0322.mediawiki#full +// It requires as input the toSpend transaction that it spends +// Transaction is build without any witness, so that the witness must be filled +// by the caller. +func GetToSignTx(toSpend *wire.MsgTx) *wire.MsgTx { + toSign := wire.NewMsgTx(toSignVersion) + toSign.LockTime = toSignLockTime + + // Specify the input outpoint + // Given that the input is the toSpend tx we have built, the input index is 0 + inputHash := toSpend.TxHash() + outPoint := wire.NewOutPoint(&inputHash, 0) + + input := wire.NewTxIn(outPoint, nil, nil) + input.Sequence = toSignInputSeq + + // Create the output + output := wire.NewTxOut(toSignOutputValue, toSignPkScript()) + + toSign.AddTxIn(input) + toSign.AddTxOut(output) + return toSign +} + +func Verify( + msg []byte, + witness wire.TxWitness, + address btcutil.Address, + net *chaincfg.Params) error { + + toSpend, err := GetToSpendTx(msg, address) + if err != nil { + return err + } + + toSign := GetToSignTx(toSpend) + + toSign.TxIn[0].Witness = witness + + // From the rules here: + // https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#verification-process + // We only need to perform verification of whether toSign spends toSpend properly + // given that the signature is a simple one and we construct both toSpend and toSign + inputFetcher := txscript.NewCannedPrevOutputFetcher(toSpend.TxOut[0].PkScript, 0) + sigHashes := txscript.NewTxSigHashes(toSign, inputFetcher) + vm, err := txscript.NewEngine( + toSpend.TxOut[0].PkScript, toSign, 0, + txscript.StandardVerifyFlags, txscript.NewSigCache(0), sigHashes, + toSpend.TxOut[0].Value, inputFetcher, + ) + + if err != nil { + return err + } + + return vm.Execute() +} + +func PubkeyToP2WPKHAddress(p *btcec.PublicKey, net *chaincfg.Params) (*btcutil.AddressWitnessPubKeyHash, error) { + witnessAddr, err := btcutil.NewAddressWitnessPubKeyHash( + btcutil.Hash160(p.SerializeCompressed()), + net, + ) + + if err != nil { + return nil, err + } + + return witnessAddr, nil +} + +func PubKeyToP2TrSpendAddress(p *btcec.PublicKey, net *chaincfg.Params) (*btcutil.AddressTaproot, error) { + tapKey := txscript.ComputeTaprootKeyNoScript(p) + + address, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(tapKey), net, + ) + if err != nil { + return nil, err + } + return address, nil +} + +func SignWithP2WPKHAddress( + msg []byte, + privKey *btcec.PrivateKey, + net *chaincfg.Params, +) (*btcutil.AddressWitnessPubKeyHash, []byte, error) { + pubKey := privKey.PubKey() + + witnessAddr, err := PubkeyToP2WPKHAddress(pubKey, net) + + if err != nil { + return nil, nil, err + } + + toSpend, err := GetToSpendTx(msg, witnessAddr) + + if err != nil { + return nil, nil, err + } + + toSign := GetToSignTx(toSpend) + + fetcher := txscript.NewCannedPrevOutputFetcher( + toSpend.TxOut[0].PkScript, + toSpend.TxOut[0].Value, + ) + + hashCache := txscript.NewTxSigHashes(toSign, fetcher) + + // always use compressed pubkey + witness, err := txscript.WitnessSignature(toSign, hashCache, 0, + toSpend.TxOut[0].Value, toSpend.TxOut[0].PkScript, txscript.SigHashAll, privKey, true) + + if err != nil { + return nil, nil, err + } + + serializedWitness, err := SerializeWitness(witness) + + if err != nil { + return nil, nil, err + } + + return witnessAddr, serializedWitness, nil +} + +func SignWithP2TrSpendAddress( + msg []byte, + privKey *btcec.PrivateKey, + net *chaincfg.Params, +) (*btcutil.AddressTaproot, []byte, error) { + pubKey := privKey.PubKey() + + witnessAddr, err := PubKeyToP2TrSpendAddress(pubKey, net) + + if err != nil { + return nil, nil, err + } + + toSpend, err := GetToSpendTx(msg, witnessAddr) + + if err != nil { + return nil, nil, err + } + + toSign := GetToSignTx(toSpend) + + fetcher := txscript.NewCannedPrevOutputFetcher( + toSpend.TxOut[0].PkScript, + toSpend.TxOut[0].Value, + ) + + hashCache := txscript.NewTxSigHashes(toSign, fetcher) + + witness, err := txscript.TaprootWitnessSignature( + toSign, hashCache, 0, toSpend.TxOut[0].Value, toSpend.TxOut[0].PkScript, + txscript.SigHashDefault, privKey, + ) + + if err != nil { + return nil, nil, err + } + + serializedWitness, err := SerializeWitness(witness) + + if err != nil { + return nil, nil, err + } + + return witnessAddr, serializedWitness, nil +} diff --git a/crypto/bip322/bip322_test.go b/crypto/bip322/bip322_test.go new file mode 100644 index 000000000..22b4f5a13 --- /dev/null +++ b/crypto/bip322/bip322_test.go @@ -0,0 +1,118 @@ +package bip322_test + +import ( + "encoding/base64" + "encoding/hex" + "math/rand" + "testing" + + "github.com/babylonchain/babylon/crypto/bip322" + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" +) + +var ( + net = &chaincfg.TestNet3Params + emptyBytes = []byte("") + helloWorldBytes = []byte("Hello World") + testAddr = "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l" + testAddrDecoded, _ = btcutil.DecodeAddress(testAddr, net) +) + +// test vectors at https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#message-hashing +func TestBIP322_MsgHash(t *testing.T) { + msgHash := bip322.GetBIP340TaggedHash(emptyBytes) + msgHashHex := hex.EncodeToString(msgHash[:]) + require.Equal(t, msgHashHex, "c90c269c4f8fcbe6880f72a721ddfbf1914268a794cbb21cfafee13770ae19f1") + + msgHash = bip322.GetBIP340TaggedHash(helloWorldBytes) + msgHashHex = hex.EncodeToString(msgHash[:]) + require.Equal(t, msgHashHex, "f0eb03b1a75ac6d9847f55c624a99169b5dccba2a31f5b23bea77ba270de0a7a") +} + +// test vectors at https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#transaction-hashes +func TestBIP322_TxHashToSpend(t *testing.T) { + // empty str + toSpendTx, err := bip322.GetToSpendTx(emptyBytes, testAddrDecoded) + require.NoError(t, err) + require.Equal(t, "c5680aa69bb8d860bf82d4e9cd3504b55dde018de765a91bb566283c545a99a7", toSpendTx.TxHash().String()) + toSignTx := bip322.GetToSignTx(toSpendTx) + require.Equal(t, "1e9654e951a5ba44c8604c4de6c67fd78a27e81dcadcfe1edf638ba3aaebaed6", toSignTx.TxHash().String()) + + // hello world str + toSpendTx, err = bip322.GetToSpendTx(helloWorldBytes, testAddrDecoded) + require.NoError(t, err) + require.Equal(t, "b79d196740ad5217771c1098fc4a4b51e0535c32236c71f1ea4d61a2d603352b", toSpendTx.TxHash().String()) + toSignTx = bip322.GetToSignTx(toSpendTx) + require.Equal(t, "88737ae86f2077145f93cc4b153ae9a1cb8d56afa511988c149c5c8c9d93bddf", toSignTx.TxHash().String()) +} + +func TestBIP322_Verify(t *testing.T) { + sigBase64 := "AkcwRAIgbAFRpM0rhdBlXr7qe5eEf3XgSeausCm2XTmZVxSYpcsCIDcbR87wF9DTrvdw1czYEEzOjso52dOSaw8VrC4GgzFRASECO5NGNFlPClJnTHNDW94h7pPL5D7xbl6FBNTrGaYpYcA=" + msgBase64 := "HRQD77+9dmnvv71N77+9O2/Wuzbvv73vv71a77+977+977+977+9Du+/ve+/vTgrNH/vv71lQX0=" + // TODO: make it work with the public key?? + address := "tb1qfwtfzdagj7efph6zfcv68ce3v48c8e9fatunur" + addressDecoded, err := btcutil.DecodeAddress(address, net) + require.NoError(t, err) + + emptyBytesSig, err := base64.StdEncoding.DecodeString(sigBase64) + require.NoError(t, err) + + msg, err := base64.StdEncoding.DecodeString(msgBase64) + require.NoError(t, err) + + witness, err := bip322.SimpleSigToWitness(emptyBytesSig) + require.NoError(t, err) + + err = bip322.Verify(msg, witness, addressDecoded, net) + require.NoError(t, err) +} + +func FuzzBip322ValidP2WPKHSignature(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + privkey, err := btcec.NewPrivateKey() + require.NoError(t, err) + dataLen := r.Int31n(200) + 1 + dataToSign := datagen.GenRandomByteArray(r, uint64(dataLen)) + address, witness, err := bip322.SignWithP2WPKHAddress(dataToSign, privkey, net) + require.NoError(t, err) + witnessDecoded, err := bip322.SimpleSigToWitness(witness) + require.NoError(t, err) + + err = bip322.Verify( + dataToSign, + witnessDecoded, + address, + net, + ) + require.NoError(t, err) + }) +} + +func FuzzBip322ValidP2TrSpendSignature(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + privkey, err := btcec.NewPrivateKey() + require.NoError(t, err) + dataLen := r.Int31n(200) + 1 + dataToSign := datagen.GenRandomByteArray(r, uint64(dataLen)) + address, witness, err := bip322.SignWithP2TrSpendAddress(dataToSign, privkey, net) + require.NoError(t, err) + witnessDecoded, err := bip322.SimpleSigToWitness(witness) + require.NoError(t, err) + + err = bip322.Verify( + dataToSign, + witnessDecoded, + address, + net, + ) + require.NoError(t, err) + }) +} diff --git a/crypto/bip322/witness.go b/crypto/bip322/witness.go new file mode 100644 index 000000000..1dc01c90b --- /dev/null +++ b/crypto/bip322/witness.go @@ -0,0 +1,134 @@ +package bip322 + +import ( + "bytes" + "fmt" + "io" + + "github.com/btcsuite/btcd/wire" +) + +// this file provides functionality for converting a simple signature to +// a witness stack. +// the entire file is adapted from https://github.com/btcsuite/btcd/blob/v0.23.4/wire/msgtx.go#L568-L599 + +const ( + // maxWitnessItemsPerInput is the maximum number of witness items to + // be read for the witness data for a single TxIn. This number is + // derived using a possible lower bound for the encoding of a witness + // item: 1 byte for length + 1 byte for the witness item itself, or two + // bytes. This value is then divided by the currently allowed maximum + // "cost" for a transaction. We use this for an upper bound for the + // buffer and consensus makes sure that the weight of a transaction + // cannot be more than 4000000. + maxWitnessItemsPerInput = 4_000_000 + + // maxWitnessItemSize is the maximum allowed size for an item within + // an input's witness data. This value is bounded by the largest + // possible block size, post segwit v1 (taproot). + maxWitnessItemSize = 4_000_000 +) + +// readScript reads a variable length byte array that represents a transaction +// script. It is encoded as a varInt containing the length of the array +// followed by the bytes themselves. An error is returned if the length is +// greater than the passed maxAllowed parameter which helps protect against +// memory exhaustion attacks and forced panics through malformed messages. The +// fieldName parameter is only used for the error message so it provides more +// context in the error. +func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) { + count, err := wire.ReadVarInt(r, pver) + if err != nil { + return nil, err + } + + // Prevent byte array larger than the max message size. It would + // be possible to cause memory exhaustion and panics without a sane + // upper bound on this count. + if count > uint64(maxAllowed) { + return nil, fmt.Errorf("%s is larger than the max allowed size "+ + "[count %d, max %d]", fieldName, count, maxAllowed) + } + + b := make([]byte, count) + _, err = io.ReadFull(r, b) + if err != nil { + return nil, err + } + return b, nil +} + +// simpleSigToWitness converts a simple signature into a witness stack +// As per the BIP-322 spec: +// "A simple signature consists of a witness stack, consensus encoded as a vector of vectors of bytes" +// https://github.com/bitcoin/bips/blob/e643d247c8bc086745f3031cdee0899803edea2f/bip-0322.mediawiki#simple +// However, the above does not provide much information about its encoding. +// We work on the encoding based on the Leather wallet implementation: +// https://github.com/leather-wallet/extension/blob/068d3cd465e1a642a763fecfa0e3ce5e94b07286/src/shared/crypto/bitcoin/bip322/bip322-utils.ts#L58 +// More specifically, the signature is encoded as follows: +// - 1st byte: Elements of the witness stack that are serialized +// - For each element of the stack +// - The first byte specifies how many bytes it contains +// - The rest are the bytes of the element +func SimpleSigToWitness(sig []byte) ([][]byte, error) { + // For each input, the witness is encoded as a stack + // with one or more items. Therefore, we first read a + // varint which encodes the number of stack items. + buf := bytes.NewBuffer(sig) + witCount, err := wire.ReadVarInt(buf, 0) + if err != nil { + return nil, err + } + + // Prevent a possible memory exhaustion attack by + // limiting the witCount value to a sane upper bound. + if witCount > maxWitnessItemsPerInput { + return nil, fmt.Errorf("too many witness items to fit "+ + "into max message size [count %d, max %d]", + witCount, maxWitnessItemsPerInput) + } + + // Then for witCount number of stack items, each item + // has a varint length prefix, followed by the witness + // item itself. + witnessStack := make([][]byte, witCount) + for j := uint64(0); j < witCount; j++ { + witnessStack[j], err = readScript( + buf, 0, maxWitnessItemSize, "script witness item", + ) + if err != nil { + return nil, err + } + } + + return witnessStack, nil +} + +// serialization of witness copied from btcd +func writeTxWitness( + w io.Writer, + wit [][]byte, +) error { + // pver is always 0 (at least in btcd) + err := wire.WriteVarInt(w, 0, uint64(len(wit))) + if err != nil { + return err + } + for _, item := range wit { + err = wire.WriteVarBytes(w, 0, item) + if err != nil { + return err + } + } + return nil +} + +func SerializeWitness(w wire.TxWitness) ([]byte, error) { + var buf bytes.Buffer + + if err := writeTxWitness(&buf, w); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/crypto/bls12381/bls.go b/crypto/bls12381/bls.go index 114db2af3..bc1e7f882 100644 --- a/crypto/bls12381/bls.go +++ b/crypto/bls12381/bls.go @@ -2,10 +2,11 @@ package bls12381 import ( "crypto/rand" - tmcrypto "github.com/cometbft/cometbft/crypto" + "io" + + cmtcrypto "github.com/cometbft/cometbft/crypto" "github.com/pkg/errors" blst "github.com/supranational/blst/bindings/go" - "io" ) // GenKeyPair generates a random BLS key pair based on a given seed @@ -23,7 +24,7 @@ func GenPrivKey() PrivateKey { } func GenPrivKeyFromSecret(secret []byte) PrivateKey { - seed := tmcrypto.Sha256(secret) + seed := cmtcrypto.Sha256(secret) return genPrivKeyFromSeed(seed) } diff --git a/crypto/ecdsa/README.md b/crypto/ecdsa/README.md new file mode 100644 index 000000000..6d389b35a --- /dev/null +++ b/crypto/ecdsa/README.md @@ -0,0 +1,11 @@ +# ECDSA + +This module implements Bitcoin's ECDSA signature algorithm over Secp256k1 curve. +It follows the "message sign" format in [Bitcoin](https://github.com/bitcoin/bitcoin/pull/524). +The format is used in [OKX wallet SDK](https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/src/BtcWallet.ts#L331). + +References: + +- [Original design and implementation](https://github.com/bitcoin/bitcoin/pull/524) +- [An unofficial spec](https://github.com/fivepiece/sign-verify-message/blob/master/signverifymessage.md) +- [Implementation of OKX wallet SDK](https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/src/BtcWallet.ts#L331) diff --git a/crypto/ecdsa/ecdsa.go b/crypto/ecdsa/ecdsa.go new file mode 100644 index 000000000..fed6dc1fb --- /dev/null +++ b/crypto/ecdsa/ecdsa.go @@ -0,0 +1,50 @@ +package ecdsa + +import ( + "bytes" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" +) + +const ( + MAGIC_MESSAGE_PREFIX = "Bitcoin Signed Message:\n" +) + +// magicHash encodes the given msg into byte array, then calculates its sha256d hash +// ref: https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/src/message.ts#L28-L34 +func magicHash(msg string) chainhash.Hash { + buf := bytes.NewBuffer(nil) + // we have to use wire.WriteVarString which encodes the string length into the byte array in Bitcoin's own way + // message prefix + // NOTE: we have control over the buffer so no need to check error + wire.WriteVarString(buf, 0, MAGIC_MESSAGE_PREFIX) //nolint:errcheck + // message + wire.WriteVarString(buf, 0, msg) //nolint:errcheck + bytes := buf.Bytes() + + return chainhash.DoubleHashH(bytes) +} + +func Sign(sk *btcec.PrivateKey, msg string) ([]byte, error) { + msgHash := magicHash(msg) + return ecdsa.SignCompact(sk, msgHash[:], true) +} + +func Verify(pk *btcec.PublicKey, msg string, sigBytes []byte) error { + msgHash := magicHash(msg) + recoveredPK, _, err := ecdsa.RecoverCompact(sigBytes, msgHash[:]) + if err != nil { + return err + } + pkBytes := schnorr.SerializePubKey(pk) + recoveredPKBytes := schnorr.SerializePubKey(recoveredPK) + if !bytes.Equal(pkBytes, recoveredPKBytes) { + return fmt.Errorf("the recovered PK does not match the given PK") + } + return nil +} diff --git a/crypto/ecdsa/ecdsa_test.go b/crypto/ecdsa/ecdsa_test.go new file mode 100644 index 000000000..836e49f8a --- /dev/null +++ b/crypto/ecdsa/ecdsa_test.go @@ -0,0 +1,38 @@ +package ecdsa_test + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "testing" + + "github.com/babylonchain/babylon/crypto/ecdsa" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/stretchr/testify/require" +) + +const ( + // test vector from https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/tests/btc.test.ts#L113-L126 + skHex = "adce25dc25ef89f06a722abdc4b601d706c9efc6bc84075355e6b96ca3871621" + testMsg = "hello world" + testSigBase64 = "IDtG3XPLpiKOp4PjTzCo/ng8gm4MFTTyHeh/DaPC1XYsYaj5Jr4h8dnxmwuJtNkPkH40rEfnrrO8fgZKNOIF5iM=" +) + +func TestECDSA(t *testing.T) { + // decode SK and PK + skBytes, err := hex.DecodeString(skHex) + require.NoError(t, err) + sk, pk := btcec.PrivKeyFromBytes(skBytes) + require.NotNil(t, sk) + require.NotNil(t, pk) + // sign + sig, err := ecdsa.Sign(sk, testMsg) + require.NoError(t, err) + testSigBytes, err := base64.StdEncoding.DecodeString(testSigBase64) + require.NoError(t, err) + // ensure sig is same as that in test vector + require.True(t, bytes.Equal(sig, testSigBytes)) + // verify + err = ecdsa.Verify(pk, testMsg, sig) + require.NoError(t, err) +} diff --git a/crypto/eots/README.md b/crypto/eots/README.md new file mode 100644 index 000000000..e2ff53a68 --- /dev/null +++ b/crypto/eots/README.md @@ -0,0 +1,3 @@ +# EOTS + +This module implements extractable one-time signature (EOTS). The code is copied from https://github.com/babylonchain/eots. \ No newline at end of file diff --git a/crypto/eots/eots.go b/crypto/eots/eots.go new file mode 100644 index 000000000..f94e66786 --- /dev/null +++ b/crypto/eots/eots.go @@ -0,0 +1,231 @@ +package eots + +import ( + "crypto/sha256" + "errors" + "io" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + ecdsa_schnorr "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" +) + +type ModNScalar = btcec.ModNScalar +type PrivateKey = secp256k1.PrivateKey +type PublicKey = secp256k1.PublicKey +type PrivateRand = secp256k1.ModNScalar +type PublicRand = secp256k1.FieldVal + +// The Signature is only the S part of the BEP-340 Schnorr signatures. +type Signature = ModNScalar + +// KeyGen generates private key from a randomness source +func KeyGen(randSource io.Reader) (*PrivateKey, error) { + return secp256k1.GeneratePrivateKeyFromRand(randSource) +} + +// PubGen returns the associated public key from a private key. +func PubGen(k *PrivateKey) *PublicKey { + return k.PubKey() +} + +// RandGen returns the value to be used as random value when signing, and the associated public value. +func RandGen(randSource io.Reader) (*PrivateRand, *PublicRand, error) { + pk, err := KeyGen(randSource) + if err != nil { + return nil, nil, err + } + var j secp256k1.JacobianPoint + pk.PubKey().AsJacobian(&j) + return &pk.Key, &j.X, nil +} + +// hash function is used for hashing the message input for all functions of the library. +// Wrapper around sha256 in order to change only one function if the input hashing function is changed. +func hash(message []byte) [32]byte { + return sha256.Sum256(message) +} + +// Sign returns an extractable Schnorr signature for a message, signed with a private key and private randomness value. +// Note that the Signature is only the second (S) part of the typical bitcoin signature, the first (R) can be deduced from +// the public randomness value and the message. +func Sign(sk *PrivateKey, privateRand *PrivateRand, message []byte) (*Signature, error) { + h := hash(message) + return signHash(sk, privateRand, h) +} + +// signHash returns an extractable Schnorr signature for a hashed message. +// The caller MUST ensure that hash is the output of a cryptographically secure hash function. +// Based on unexported schnorrSign of btcd. +func signHash(sk *PrivateKey, privateRand *PrivateRand, hash [32]byte) (*Signature, error) { + if sk.Key.IsZero() { + str := "private key is zero" + return nil, signatureError(ecdsa_schnorr.ErrPrivateKeyIsZero, str) + } + + // d' = int(d) + var privKeyScalar ModNScalar + privKeyScalar.Set(&sk.Key) + + pubKey := PubGen(sk) + + // Negate d if P.y is odd. + pubKeyBytes := pubKey.SerializeCompressed() + if pubKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { + privKeyScalar.Negate() + } + + k := new(ModNScalar).Set(privateRand) + + // R = kG + var R btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(k, &R) + + // Negate nonce k if R.y is odd (R.y is the y coordinate of the point R) + // + // Note that R must be in affine coordinates for this check. + R.ToAffine() + if R.Y.IsOdd() { + k.Negate() + } + + // e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n + var rBytes [32]byte + r := &R.X + r.PutBytesUnchecked(rBytes[:]) + pBytes := pubKey.SerializeCompressed()[1:] + + commitment := chainhash.TaggedHash(chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash[:]) + + var e ModNScalar + if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { + k.Zero() + str := "hash of (r || P || m) too big" + return nil, signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) + } + + // s = k + e*d mod n + sig := new(ModNScalar).Mul2(&e, &privKeyScalar).Add(k) + + // If Verify(bytes(P), m, sig) fails, abort. + // optional + + // Return s + return sig, nil +} + +// Verify verifies that the signature is valid for this message, public key and random value. +func Verify(pubKey *PublicKey, r *PublicRand, message []byte, sig *Signature) error { + h := hash(message) + return verifyHash(pubKey, r, h, sig) +} + +// Verify verifies that the signature is valid for this hashed message, public key and random value. +// Based on unexported schnorrVerify of btcd. +func verifyHash(pubKey *PublicKey, r *PublicRand, hash [32]byte, sig *Signature) error { + // Fail if P is not a point on the curve + if !pubKey.IsOnCurve() { + str := "pubkey point is not on curve" + return signatureError(ecdsa_schnorr.ErrPubKeyNotOnCurve, str) + } + + // Fail if r >= p is already handled by the fact r is a field element. + // Fail if s >= n is already handled by the fact s is a mod n scalar. + + // e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n. + var rBytes [32]byte + r.PutBytesUnchecked(rBytes[:]) + pBytes := pubKey.SerializeCompressed()[1:] + + commitment := chainhash.TaggedHash(chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash[:]) + + var e ModNScalar + if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { + str := "hash of (r || P || m) too big" + return signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) + } + + // Negate e here so we can use AddNonConst below to subtract the s*G + // point from e*P. + e.Negate() + + // R = s*G - e*P + var P, R, sG, eP btcec.JacobianPoint + pubKey.AsJacobian(&P) + btcec.ScalarBaseMultNonConst(sig, &sG) + btcec.ScalarMultNonConst(&e, &P, &eP) + btcec.AddNonConst(&sG, &eP, &R) + + // Fail if R is the point at infinity + if (R.X.IsZero() && R.Y.IsZero()) || R.Z.IsZero() { + str := "calculated R point is the point at infinity" + return signatureError(ecdsa_schnorr.ErrSigRNotOnCurve, str) + } + + // Fail if R.y is odd + // + // Note that R must be in affine coordinates for this check. + R.ToAffine() + if R.Y.IsOdd() { + str := "calculated R y-value is odd" + return signatureError(ecdsa_schnorr.ErrSigRYIsOdd, str) + } + + // verify signed with the right k random value + if !r.Equals(&R.X) { + str := "calculated R point was not given R" + return signatureError(ecdsa_schnorr.ErrUnequalRValues, str) + } + + return nil +} + +// Extract extracts the private key from a public key and signatures for two distinct hashes messages. +func Extract(pubKey *PublicKey, r *PublicRand, message1 []byte, sig1 *Signature, message2 []byte, sig2 *Signature) (*PrivateKey, error) { + h1 := hash(message1) + h2 := hash(message2) + return extractFromHashes(pubKey, r, h1, sig1, h2, sig2) +} + +// extractFromHashes extracts the private key from hashes, instead of the non-hashed message directly as Extract does. +func extractFromHashes(pubKey *PublicKey, r *PublicRand, hash1 [32]byte, sig1 *Signature, hash2 [32]byte, sig2 *Signature) (*PrivateKey, error) { + var rBytes [32]byte + r.PutBytesUnchecked(rBytes[:]) + pBytes := pubKey.SerializeCompressed()[1:] + + if sig1.Equals(sig2) { + return nil, errors.New("The two signatures need to be different in order to extract") + } + + commitment1 := chainhash.TaggedHash(chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash1[:]) + var e1 ModNScalar + if overflow := e1.SetBytes((*[32]byte)(commitment1)); overflow != 0 { + str := "hash of (r || P || m1) too big" + return nil, signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) + } + + commitment2 := chainhash.TaggedHash(chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash2[:]) + var e2 ModNScalar + if overflow := e2.SetBytes((*[32]byte)(commitment2)); overflow != 0 { + str := "hash of (r || P || m2) too big" + return nil, signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) + } + + // x = (s1 - s2) / (e1 - e2) + var x, denom ModNScalar + denom.Add2(&e1, e2.Negate()) + x.Add2(sig1, sig2.Negate()).Mul(denom.InverseNonConst()) + + pubKeyBytes := pubKey.SerializeCompressed() + if pubKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { + x.Negate() + } + + privKey := secp256k1.NewPrivateKey(&x) + if privKey.PubKey().IsEqual(pubKey) { + return privKey, nil + } else { + return privKey, errors.New("Extracted private key does not match public key") + } +} diff --git a/crypto/eots/eots_test.go b/crypto/eots/eots_test.go new file mode 100644 index 000000000..9adc8df8f --- /dev/null +++ b/crypto/eots/eots_test.go @@ -0,0 +1,124 @@ +package eots + +import ( + "bytes" + "crypto/rand" + "testing" + + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/vulpine-io/io-test/v1/pkg/iotest" +) + +// TODO: possible improvements +// test KeyGen, PubGen, RandGen give consistent results with deterministic randomness source +// test compare signatures against btcec + +func FuzzSignAndVerify(f *testing.F) { + randSource := new(iotest.ReadCloser) + + sk, err := KeyGen(randSource) + if err != nil { + f.Fatal(err) + } + k, publicK, err := RandGen(rand.Reader) + if err != nil { + f.Fatal(err) + } + + for _, seed := range [][]byte{[]byte("hello"), []byte("1234567890!@#$%^&*()")} { + f.Add(seed) + } + f.Fuzz(func(t *testing.T, message []byte) { + sig, err := Sign(sk, k, message) + if err != nil { + t.Fatal(err) + } + err = Verify(PubGen(sk), publicK, message, sig) + if err != nil { + t.Fatal(err) + } + }) +} + +func TestSignAndInvalidVerify(t *testing.T) { + randSource := new(iotest.ReadCloser) + sk, err := KeyGen(randSource) + if err != nil { + t.Fatal(err) + } + k, publicK, err := RandGen(randSource) + if err != nil { + t.Fatal(err) + } + + message := []byte("hello") + + sig, err := Sign(sk, k, message) + if err != nil { + t.Fatal(err) + } + + invalidK := new(secp256k1.FieldVal).Set(publicK).AddInt(1) + + err = Verify(PubGen(sk), invalidK, message, sig) + if err == nil { + t.Fatal("Expected verify to fail with wrong k value") + } + + err = Verify(PubGen(sk), publicK, message, new(Signature)) + if err == nil { + t.Fatal("Expected verify to fail with wrong signature for the hash") + } + + messageInvalid := []byte("bye") + err = Verify(PubGen(sk), publicK, messageInvalid, sig) + if err == nil { + t.Fatal("Expected verify to fail with wrong signature for the hash") + } +} + +func FuzzExtract(f *testing.F) { + randSource := new(iotest.ReadCloser) + sk, err := KeyGen(randSource) + if err != nil { + f.Fatal(err) + } + k, publicK, err := RandGen(rand.Reader) + if err != nil { + f.Fatal(err) + } + + type tc struct { + m1 []byte + m2 []byte + } + + for _, seed := range []tc{{[]byte("hello"), []byte("bye")}, {[]byte("1234567890"), []byte("!@#$%^&*()")}} { + f.Add(seed.m1, seed.m2) + } + + f.Fuzz(func(t *testing.T, message1, message2 []byte) { + if bytes.Equal(message1, message2) { + t.Skip() + } + + sig1, err := Sign(sk, k, message1) + if err != nil { + t.Fatal(err) + } + + sig2, err := Sign(sk, k, message2) + if err != nil { + t.Fatal(err) + } + + sk2, err := Extract(sk.PubKey(), publicK, message1, sig1, message2, sig2) + if err != nil { + t.Fatal(err) + } + + if !sk.Key.Equals(&sk2.Key) { + t.Fatal("Unexpected extracted private key") + } + }) +} diff --git a/crypto/eots/error.go b/crypto/eots/error.go new file mode 100644 index 000000000..831587a2c --- /dev/null +++ b/crypto/eots/error.go @@ -0,0 +1,20 @@ +package eots + +import ( + ecdsa_schnorr "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" +) + +// ErrorKind identifies a kind of error. It has full support for errors.Is +// and errors.As, so the caller can directly check against an error kind +// when determining the reason for an error. +type ErrorKind = ecdsa_schnorr.ErrorKind + +// Error identifies an error related to a schnorr signature. It has full +// support for errors.Is and errors.As, so the caller can ascertain the +// specific reason for the error by checking the underlying error. +type Error = ecdsa_schnorr.Error + +// signatureError creates an Error given a set of arguments. +func signatureError(kind ErrorKind, desc string) Error { + return Error{Err: kind, Description: desc} +} diff --git a/crypto/eots/examples/btc-testnet-txs/main.go b/crypto/eots/examples/btc-testnet-txs/main.go new file mode 100644 index 000000000..2ddec8443 --- /dev/null +++ b/crypto/eots/examples/btc-testnet-txs/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "flag" + "fmt" + "os" + + "github.com/babylonchain/babylon/crypto/eots" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +func main() { + initialSecretKey, amount, unspentTxHash, outIndex, fees := parseFlags() + + // generate keys for first tx + fmt.Println("Generating extractable keys (address A)") + secretKey, publicKey, privateRand, publicRand, wif1, address1 := generateKeys() + fmt.Println("Address A: ", address1.EncodeAddress()) + fmt.Println("Address A private key (WIF):", wif1.String()) + + // generate a transaction + fmt.Println("\nGenerating first transaction spendable by extractable key...") + txHash1, tx1, err := generateTx(initialSecretKey, amount, fees, address1, unspentTxHash, outIndex) + if err != nil { + panic(err) + } + fmt.Println("First tx hash: ", txHash1) + fmt.Println("First tx encoded: ", tx1) + + h1, s1, h2, s2 := generateSignatures(secretKey, privateRand, "1", "2") + + // the secret key is no longer used + secretKey = nil + + fmt.Println("---") + fmt.Println("Extracting key...") + + extractedKey, _ := eots.Extract(publicKey, publicRand, h1, s1, h2, s2) + + fmt.Println("Key extracted") + + fmt.Println("Generating keys for address B") + _, _, _, _, wif2, address2 := generateKeys() + fmt.Println("Address B: ", address2.EncodeAddress()) + fmt.Println("Private key for address B: ", wif2.String()) + + fmt.Println("\nGenerating transaction that spends BTC using the extracted key...") + txHash2, tx2, err := generateTx(extractedKey, amount-fees, fees, address2, txHash1, 0) + if err != nil { + panic(err) + } + fmt.Println("Second tx hash:", txHash2) + fmt.Println("Second tx encoded:", tx2) +} + +func generateKeys() (*eots.PrivateKey, *eots.PublicKey, *eots.PrivateRand, *eots.PublicRand, *btcutil.WIF, *btcutil.AddressPubKeyHash) { + secretKey, err := eots.KeyGen(rand.Reader) + if err != nil { + panic(err) + } + + publicKey := eots.PubGen(secretKey) + + privateRand, publicRand, err := eots.RandGen(rand.Reader) + if err != nil { + panic(err) + } + + wif, err := btcutil.NewWIF(secretKey, &chaincfg.TestNet3Params, true) + if err != nil { + panic(err) + } + + address, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(publicKey.SerializeCompressed()), &chaincfg.TestNet3Params) + if err != nil { + panic(err) + } + + return secretKey, publicKey, privateRand, publicRand, wif, address +} + +func generateSignatures(secretKey *eots.PrivateKey, privateRand *eots.PrivateRand, message1, message2 string) ([]byte, *eots.Signature, []byte, *eots.Signature) { + h1 := chainhash.HashB([]byte(message1)) + s1, _ := eots.Sign(secretKey, privateRand, h1) + + h2 := chainhash.HashB([]byte(message2)) + s2, _ := eots.Sign(secretKey, privateRand, h2) + + return h1, s1, h2, s2 +} + +func generateTx(senderPk *eots.PrivateKey, amount int64, fees int64, rcvAddress *btcutil.AddressPubKeyHash, unspentTxId *chainhash.Hash, outIndex uint32) (*chainhash.Hash, string, error) { + recTx := wire.NewMsgTx(wire.TxVersion) + + outPoint := wire.NewOutPoint(unspentTxId, outIndex) + txIn := wire.NewTxIn(outPoint, nil, nil) + recTx.AddTxIn(txIn) + + rcvScript2, err := txscript.PayToAddrScript(rcvAddress) + if err != nil { + return nil, "", err + } + outCoin := int64(amount - fees) + txOut := wire.NewTxOut(outCoin, rcvScript2) + recTx.AddTxOut(txOut) + + senderAddress, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(senderPk.PubKey().SerializeCompressed()), &chaincfg.TestNet3Params) + rcvScript, err := txscript.PayToAddrScript(senderAddress) + if err != nil { + return nil, "", err + } + + scriptSig, err := txscript.SignatureScript( + recTx, + 0, + rcvScript, + txscript.SigHashAll, + senderPk, + true) + if err != nil { + return nil, "", err + } + recTx.TxIn[0].SignatureScript = scriptSig + + buf := bytes.NewBuffer(make([]byte, 0, recTx.SerializeSize())) + recTx.Serialize(buf) + + // verify transaction + vm, err := txscript.NewEngine(rcvScript, recTx, 0, txscript.StandardVerifyFlags, nil, nil, amount, nil) + if err != nil { + return nil, "", err + } + if err := vm.Execute(); err != nil { + return nil, "", err + } + + hash := recTx.TxHash() + return &hash, hex.EncodeToString(buf.Bytes()), nil +} + +func parseFlags() (*eots.PrivateKey, int64, *chainhash.Hash, uint32, int64) { + keyWif := flag.String("key", "", "Private key used for spending utxo") + + amount := flag.Int64("amount", 0, "Amount available to spend (including fees)") + + unspentTxId := flag.String("tx", "", "Hash of unspent transaction") + + outIndex := flag.Int("outIndex", 0, "Output index of unspent transaction to spend (default 0)") + + fees := flag.Int64("fees", 1000, "Amount to be left for fees in each of the two transactions (default: 1000 satoshi)") + + flag.Parse() + + if *keyWif == "" { + fmt.Println("Please provide key (-key=WIF_ENCODED_KEY)") + os.Exit(1) + } + if *amount == 0 { + fmt.Println("Please provide amount (-amount=AMOUNT)") + os.Exit(1) + } + if *unspentTxId == "" { + fmt.Println("Please provide unspent transaction hash (-tx=HASH)") + os.Exit(1) + } + + wif, err := btcutil.DecodeWIF(*keyWif) + + unspentTxHash, err := chainhash.NewHashFromStr(*unspentTxId) + if err != nil { + panic(err) + } + + return wif.PrivKey, *amount, unspentTxHash, uint32(*outIndex), *fees +} diff --git a/crypto/schnorr-adaptor-signature/README.md b/crypto/schnorr-adaptor-signature/README.md new file mode 100644 index 000000000..8d6b043fd --- /dev/null +++ b/crypto/schnorr-adaptor-signature/README.md @@ -0,0 +1,5 @@ +# schnorr-adaptor-signature + +This package provides an implementation of the Schnorr adaptor signature in Golang. +It follows the construction in paper [One-Time Verifiably Encrypted Signatures A.K.A. Adaptor Signatures](https://github.com/LLFourn/one-time-VES/tree/master). +The implementation strictly ports the Rust implementation in [secp256kfun](https://github.com/LLFourn/secp256kfun/blob/master/schnorr_fun/src/adaptor/mod.rs). diff --git a/crypto/schnorr-adaptor-signature/keys.go b/crypto/schnorr-adaptor-signature/keys.go new file mode 100644 index 000000000..177a6156d --- /dev/null +++ b/crypto/schnorr-adaptor-signature/keys.go @@ -0,0 +1,128 @@ +package schnorr_adaptor_signature + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +// DecryptionKey is the decryption key in the adaptor +// signature scheme, noted by t in the paper +type DecryptionKey struct { + btcec.ModNScalar +} + +func NewDecyptionKeyFromModNScalar(scalar *btcec.ModNScalar) (*DecryptionKey, error) { + if scalar.IsZero() { + return nil, fmt.Errorf("the given scalar is zero") + } + + // enforce using a scalar corresponding to an even encryption key + var ekPoint btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(scalar, &ekPoint) + ekPoint.ToAffine() + if ekPoint.Y.IsOdd() { + scalar = scalar.Negate() + } + + return &DecryptionKey{*scalar}, nil +} + +func NewDecyptionKeyFromBTCSK(btcSK *btcec.PrivateKey) (*DecryptionKey, error) { + return NewDecyptionKeyFromModNScalar(&btcSK.Key) +} + +func NewDecyptionKeyFromBytes(decKeyBytes []byte) (*DecryptionKey, error) { + if len(decKeyBytes) != ModNScalarSize { + return nil, fmt.Errorf( + "the length of the given bytes for decryption key is incorrect (expected: %d, actual: %d)", + ModNScalarSize, + len(decKeyBytes), + ) + } + + var decKeyScalar btcec.ModNScalar + decKeyScalar.SetByteSlice(decKeyBytes) //nolint:errcheck + + return NewDecyptionKeyFromModNScalar(&decKeyScalar) +} + +func (dk *DecryptionKey) GetEncKey() *EncryptionKey { + var ekPoint btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(&dk.ModNScalar, &ekPoint) + // NOTE: we convert ekPoint to affine coordinates for consistency + ekPoint.ToAffine() + return &EncryptionKey{ekPoint} +} + +func (dk *DecryptionKey) ToBTCSK() *btcec.PrivateKey { + return &btcec.PrivateKey{Key: dk.ModNScalar} +} + +func (dk *DecryptionKey) ToBytes() []byte { + scalarBytes := dk.ModNScalar.Bytes() + return scalarBytes[:] +} + +type EncryptionKey struct { + btcec.JacobianPoint +} + +func NewEncryptionKeyFromJacobianPoint(point *btcec.JacobianPoint) (*EncryptionKey, error) { + // ensure the point is not at infinity + if (point.X.IsZero() && point.Y.IsZero()) || point.Z.IsZero() { + return nil, fmt.Errorf("the given Jacobian point is at infinity") + } + + // convert point to affine coordinates if necessary + affinePoint := *point + if !affinePoint.Z.IsOne() { + affinePoint.ToAffine() + } + + // enforce affinePoint to be an even point + // this is needed since we cannot predict whether the given + // point or public key is odd or even + if affinePoint.Y.IsOdd() { + affinePoint.Y.Negate(1).Normalize() + } + + return &EncryptionKey{affinePoint}, nil +} + +func NewEncryptionKeyFromBTCPK(btcPK *btcec.PublicKey) (*EncryptionKey, error) { + var btcPKPoint btcec.JacobianPoint + btcPK.AsJacobian(&btcPKPoint) + return NewEncryptionKeyFromJacobianPoint(&btcPKPoint) +} + +func NewEncryptionKeyFromBytes(encKeyBytes []byte) (*EncryptionKey, error) { + point, err := btcec.ParseJacobian(encKeyBytes) + if err != nil { + return nil, err + } + return NewEncryptionKeyFromJacobianPoint(&point) +} + +func (ek *EncryptionKey) ToBTCPK() *btcec.PublicKey { + affineEK := *ek + return secp256k1.NewPublicKey(&affineEK.X, &affineEK.Y) +} + +func (ek *EncryptionKey) ToBytes() []byte { + return btcec.JacobianToByteSlice(ek.JacobianPoint) +} + +func GenKeyPair() (*EncryptionKey, *DecryptionKey, error) { + sk, err := btcec.NewPrivateKey() + if err != nil { + return nil, nil, err + } + dk, err := NewDecyptionKeyFromBTCSK(sk) + if err != nil { + return nil, nil, err + } + ek := dk.GetEncKey() + return ek, dk, nil +} diff --git a/crypto/schnorr-adaptor-signature/keys_test.go b/crypto/schnorr-adaptor-signature/keys_test.go new file mode 100644 index 000000000..dcc1bcdf8 --- /dev/null +++ b/crypto/schnorr-adaptor-signature/keys_test.go @@ -0,0 +1,68 @@ +package schnorr_adaptor_signature_test + +import ( + "testing" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + "github.com/stretchr/testify/require" +) + +func FuzzKeyGen(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, seed []byte) { + encKey, decKey, err := asig.GenKeyPair() + require.NoError(t, err) + + // ensure that decKey.GetEncKey() is same as encKey + actualEncKey := decKey.GetEncKey() + require.Equal(t, encKey, actualEncKey) + + // ensure that the corresponding btcPK and btcSK + // constitute a key pair + btcPK := encKey.ToBTCPK() + btcSK := decKey.ToBTCSK() + actualBTCPK := btcSK.PubKey() + require.Equal(t, btcPK, actualBTCPK) + + // ensure that one can convert btcPK and btcSK back to + // encKey and decKey + actualEncKey, err = asig.NewEncryptionKeyFromBTCPK(btcPK) + require.NoError(t, err) + require.Equal(t, encKey, actualEncKey) + actualDecKey, err := asig.NewDecyptionKeyFromBTCSK(btcSK) + require.NoError(t, err) + require.Equal(t, decKey, actualDecKey) + }) +} + +func FuzzKeySerialization(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, seed []byte) { + encKey, decKey, err := asig.GenKeyPair() + require.NoError(t, err) + + // roundtrip of serialising/deserialising encKey + encKeyBytes := encKey.ToBytes() + actualEncKey, err := asig.NewEncryptionKeyFromBytes(encKeyBytes) + require.NoError(t, err) + require.Equal(t, encKey, actualEncKey) + + // roundtrip of serialising/deserialising decKey + decKeyBytes := decKey.ToBytes() + actualDecKey, err := asig.NewDecyptionKeyFromBytes(decKeyBytes) + require.NoError(t, err) + require.Equal(t, decKey, actualDecKey) + }) +} diff --git a/crypto/schnorr-adaptor-signature/sig.go b/crypto/schnorr-adaptor-signature/sig.go new file mode 100644 index 000000000..09cebcfd8 --- /dev/null +++ b/crypto/schnorr-adaptor-signature/sig.go @@ -0,0 +1,241 @@ +package schnorr_adaptor_signature + +import ( + "bytes" + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + secp "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +var ( + // rfc6979ExtraDataV0 is the extra data to feed to RFC6979 when + // generating the deterministic nonce for the BIP-340 scheme. This + // ensures the same nonce is not generated for the same message and key + // as for other signing algorithms such as ECDSA. + // + // It is equal to SHA-256([]byte("BIP-340")). + rfc6979ExtraDataV0 = [chainhash.HashSize]uint8{ + 0xa3, 0xeb, 0x4c, 0x18, 0x2f, 0xae, 0x7e, 0xf4, + 0xe8, 0x10, 0xc6, 0xee, 0x13, 0xb0, 0xe9, 0x26, + 0x68, 0x6d, 0x71, 0xe8, 0x7f, 0x39, 0x4f, 0x79, + 0x9c, 0x00, 0xa5, 0x21, 0x03, 0xcb, 0x4e, 0x17, + } +) + +// AdaptorSignature is the structure for an adaptor signature +// the adaptor signature is a triple (R, s', need_negation) where +// - `R` is the tweaked public randomness, which is derived from +// offsetting public randomness R' sampled by the signer by +// using encryption key T +// - `sHat` is the secret s' in the adaptor signature +// - `needNegation` is a bool value indicating whether decryption +// key needs to be negated when decrypting a Schnorr signature +// It is needed since (R, s') does not tell whether R'+T has odd +// or even y index, thus does not tell whether decryption key needs +// to be negated upon decryption. +type AdaptorSignature struct { + r btcec.JacobianPoint + sHat btcec.ModNScalar + needNegation bool +} + +func newAdaptorSignature(r *btcec.JacobianPoint, sHat *btcec.ModNScalar, needNegation bool) *AdaptorSignature { + var sig AdaptorSignature + sig.r.Set(r) + sig.sHat.Set(sHat) + sig.needNegation = needNegation + return &sig +} + +// EncVerify verifies that the adaptor signature is valid w.r.t. the given +// public key, encryption key and message hash +func (sig *AdaptorSignature) EncVerify(pk *btcec.PublicKey, encKey *EncryptionKey, msgHash []byte) error { + pkBytes := schnorr.SerializePubKey(pk) + return encVerify(sig, msgHash, pkBytes, &encKey.JacobianPoint) +} + +// Decrypt decrypts the adaptor signature to a Schnorr signature by +// using the decryption key `decKey`, noted by `t` in the paper +func (sig *AdaptorSignature) Decrypt(decKey *DecryptionKey) *schnorr.Signature { + R := sig.r + + t := decKey.ModNScalar + if sig.needNegation { + t.Negate() + } + // s = s' + t (or s'-t if negation is needed) + s := sig.sHat + s.Add(&t) + + return schnorr.NewSignature(&R.X, &s) +} + +// Recover recovers the decryption key by using the adaptor signature +// and the Schnorr signature decrypted from it +func (sig *AdaptorSignature) Recover(decryptedSchnorrSig *schnorr.Signature) *DecryptionKey { + // unpack s and R from Schnorr signature + _, s := unpackSchnorrSig(decryptedSchnorrSig) + sHat := sig.sHat + + // extract encryption key t = s - s' + sHat.Negate() + t := s.Add(&sHat) + + if sig.needNegation { + t.Negate() + } + + return &DecryptionKey{*t} +} + +// Marshal is to implement proto interface +func (sig *AdaptorSignature) Marshal() ([]byte, error) { + if sig == nil { + return nil, nil + } + var asigBytes []byte + // append r + rBytes := btcec.JacobianToByteSlice(sig.r) + asigBytes = append(asigBytes, rBytes...) + // append sHat + sHatBytes := sig.sHat.Bytes() + asigBytes = append(asigBytes, sHatBytes[:]...) + // append needNegation + if sig.needNegation { + asigBytes = append(asigBytes, 0x01) + } else { + asigBytes = append(asigBytes, 0x00) + } + return asigBytes, nil +} + +func (sig *AdaptorSignature) MustMarshal() []byte { + if sig == nil { + return nil + } + bz, err := sig.Marshal() + if err != nil { + panic(err) + } + + return bz +} + +func (sig *AdaptorSignature) MarshalHex() string { + return hex.EncodeToString(sig.MustMarshal()) +} + +// Size is to implement proto interface +func (sig *AdaptorSignature) Size() int { + return AdaptorSignatureSize +} + +// MarshalTo is to implement proto interface +func (sig *AdaptorSignature) MarshalTo(data []byte) (int, error) { + bz, err := sig.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +// Unmarshal is to implement proto interface +func (sig *AdaptorSignature) Unmarshal(data []byte) error { + adaptorSig, err := NewAdaptorSignatureFromBytes(data) + if err != nil { + return err + } + + *sig = *adaptorSig + + return nil +} + +func (sig *AdaptorSignature) Equals(sig2 AdaptorSignature) bool { + return bytes.Equal(sig.MustMarshal(), sig2.MustMarshal()) +} + +// EncSign generates an adaptor signature by using the given secret key, +// encryption key (noted by `T` in the paper) and message hash +func EncSign(sk *btcec.PrivateKey, encKey *EncryptionKey, msgHash []byte) (*AdaptorSignature, error) { + // d' = int(d) + var skScalar btcec.ModNScalar + skScalar.Set(&sk.Key) + + // Fail if msgHash is not 32 bytes + if len(msgHash) != chainhash.HashSize { + return nil, fmt.Errorf("wrong size for message hash (got %v, want %v)", len(msgHash), chainhash.HashSize) + } + + // Fail if d = 0 or d >= n + if skScalar.IsZero() { + return nil, fmt.Errorf("private key is zero") + } + + // P = 'd*G + pk := sk.PubKey() + + // Negate d if P.y is odd. + pubKeyBytes := pk.SerializeCompressed() + if pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd { + skScalar.Negate() + } + + var privKeyBytes [chainhash.HashSize]byte + skScalar.PutBytes(&privKeyBytes) + for iteration := uint32(0); ; iteration++ { + // Use RFC6979 to generate a deterministic nonce in [1, n-1] + // parameterized by the private key, message being signed, extra data + // that identifies the scheme, and an iteration count + nonce := btcec.NonceRFC6979( + privKeyBytes[:], msgHash, rfc6979ExtraDataV0[:], nil, iteration, + ) + + // try to generate adaptor signature + sig, err := encSign(&skScalar, nonce, pk, msgHash, &encKey.JacobianPoint) + if err != nil { + // Try again with a new nonce. + continue + } + + return sig, nil + } +} + +// NewAdaptorSignatureFromBytes parses the given byte array to an adaptor signature +func NewAdaptorSignatureFromBytes(asigBytes []byte) (*AdaptorSignature, error) { + if len(asigBytes) != AdaptorSignatureSize { + return nil, fmt.Errorf( + "the length of the given bytes for adaptor signature is incorrect (expected: %d, actual: %d)", + AdaptorSignatureSize, + len(asigBytes), + ) + } + + // extract r + r, err := btcec.ParseJacobian(asigBytes[0:JacobianPointSize]) + if err != nil { + return nil, err + } + // extract sHat + var sHat btcec.ModNScalar + sHat.SetByteSlice(asigBytes[JacobianPointSize : JacobianPointSize+ModNScalarSize]) //nolint:errcheck + // extract needNegation + needNegation := asigBytes[AdaptorSignatureSize-1] != 0x00 + + return newAdaptorSignature(&r, &sHat, needNegation), nil +} + +// NewAdaptorSignatureFromHex parses the given hex string to an adaptor signature +func NewAdaptorSignatureFromHex(asigHex string) (*AdaptorSignature, error) { + asigBytes, err := hex.DecodeString(asigHex) + if err != nil { + return nil, err + } + return NewAdaptorSignatureFromBytes(asigBytes) +} diff --git a/crypto/schnorr-adaptor-signature/sig_test.go b/crypto/schnorr-adaptor-signature/sig_test.go new file mode 100644 index 000000000..34ee6cd49 --- /dev/null +++ b/crypto/schnorr-adaptor-signature/sig_test.go @@ -0,0 +1,154 @@ +package schnorr_adaptor_signature_test + +import ( + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/stretchr/testify/require" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" +) + +func FuzzEncSignAndEncVerify(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, msg []byte) { + // key pair + sk, err := btcec.NewPrivateKey() + require.NoError(t, err) + pk := sk.PubKey() + + // encryption/decryption pair + encKey, _, err := asig.GenKeyPair() + require.NoError(t, err) + + // message hash + msgHash := chainhash.HashB(msg) + + // encSign message + adaptorSig, err := asig.EncSign(sk, encKey, msgHash) + require.NoError(t, err) + + // encVerify message + err = adaptorSig.EncVerify(pk, encKey, msgHash) + require.NoError(t, err) + }) +} + +func FuzzDecrypt(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, msg []byte) { + // key pair + sk, err := btcec.NewPrivateKey() + require.NoError(t, err) + pk := sk.PubKey() + + // encryption/decryption key pair + encKey, decKey, err := asig.GenKeyPair() + require.NoError(t, err) + + // message hash + msgHash := chainhash.HashB(msg) + + // encSign message + adaptorSig, err := asig.EncSign(sk, encKey, msgHash) + require.NoError(t, err) + + // decrypt message + schnorrSig := adaptorSig.Decrypt(decKey) + + // decrypted Schnorr signature should be valid + resVerify := schnorrSig.Verify(msgHash, pk) + require.True(t, resVerify) + }) +} + +func FuzzRecover(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, msg []byte) { + // key pair + sk, err := btcec.NewPrivateKey() + require.NoError(t, err) + + // encryption/decryption key pair + encKey, decKey, err := asig.GenKeyPair() + require.NoError(t, err) + + // message hash + msgHash := chainhash.HashB(msg) + + // encSign message + adaptorSig, err := asig.EncSign(sk, encKey, msgHash) + require.NoError(t, err) + + // decrypt message + schnorrSig := adaptorSig.Decrypt(decKey) + + // recover + expectedDecKey := adaptorSig.Recover(schnorrSig) + + // assert the recovered decryption key is the expected one + require.True(t, expectedDecKey.Equals(&decKey.ModNScalar)) + }) +} + +func FuzzSerializeAdaptorSig(f *testing.F) { + // random seeds + f.Add([]byte("hello")) + f.Add([]byte("1234567890!@#$%^&*()")) + f.Add([]byte("1234567891!@#$%^&*()")) + f.Add([]byte("1234567892!@#$%^&*()")) + f.Add([]byte("1234567893!@#$%^&*()")) + + f.Fuzz(func(t *testing.T, msg []byte) { + // key pair + sk, err := btcec.NewPrivateKey() + require.NoError(t, err) + + // encryption/decryption key pair + encKey, _, err := asig.GenKeyPair() + require.NoError(t, err) + + // message hash + msgHash := chainhash.HashB(msg) + + // encSign message + adaptorSig, err := asig.EncSign(sk, encKey, msgHash) + require.NoError(t, err) + + // roundtrip for serialising/deserialising adaptor signature + adaptorSigBytes, err := adaptorSig.Marshal() + require.NoError(t, err) + var unmarshalledSig asig.AdaptorSignature + err = unmarshalledSig.Unmarshal(adaptorSigBytes) + require.NoError(t, err) + require.True(t, adaptorSig.Equals(unmarshalledSig)) + + fromBytesSig, err := asig.NewAdaptorSignatureFromBytes(adaptorSigBytes) + require.NoError(t, err) + require.True(t, adaptorSig.Equals(*fromBytesSig)) + + sigHex := adaptorSig.MarshalHex() + fromHexSig, err := asig.NewAdaptorSignatureFromHex(sigHex) + require.NoError(t, err) + require.True(t, adaptorSig.Equals(*fromHexSig)) + }) +} diff --git a/crypto/schnorr-adaptor-signature/sign_utils.go b/crypto/schnorr-adaptor-signature/sign_utils.go new file mode 100644 index 000000000..e45dc4caf --- /dev/null +++ b/crypto/schnorr-adaptor-signature/sign_utils.go @@ -0,0 +1,165 @@ +package schnorr_adaptor_signature + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" +) + +const ( + ModNScalarSize = 32 + FieldValSize = 32 + JacobianPointSize = 33 + AdaptorSignatureSize = JacobianPointSize + ModNScalarSize + 1 +) + +func encSign(privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, m []byte, T *btcec.JacobianPoint) (*AdaptorSignature, error) { + // R' = kG + var RHat btcec.JacobianPoint + k := *nonce + btcec.ScalarBaseMultNonConst(&k, &RHat) + + // get R = R'+T + var R btcec.JacobianPoint + btcec.AddNonConst(&RHat, T, &R) + // negate k and R if R.y is odd + affineRWithEvenY, needNegation := intoPointWithEvenY(&R) + R = *affineRWithEvenY + if needNegation { + k.Negate() + } + + // e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n + var rBytes [chainhash.HashSize]byte + r := &R.X + r.PutBytesUnchecked(rBytes[:]) + pBytes := schnorr.SerializePubKey(pubKey) + commitment := chainhash.TaggedHash( + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, m, + ) + var e btcec.ModNScalar + e.SetBytes((*[ModNScalarSize]byte)(commitment)) + + // s' = k + e*d mod n + sHat := new(btcec.ModNScalar).Mul2(&e, privKey).Add(&k) + + // compose signature + sig := newAdaptorSignature(&R, sHat, needNegation) + + // perform verification here. Failing to verify the generated signature + // can only be because of bad nonces. The caller function `EncSign` will + // keep trying `encSign` until finding a nonce that generates correct + // signature + if err := encVerify(sig, m, pBytes, T); err != nil { + return nil, fmt.Errorf("the provided nonce does not work: %w", err) + } + + // Return signature + return sig, nil +} + +func encVerify(sig *AdaptorSignature, m []byte, pubKeyBytes []byte, T *btcec.JacobianPoint) error { + // Fail if m is not 32 bytes + if len(m) != chainhash.HashSize { + return fmt.Errorf("wrong size for message (got %v, want %v)", + len(m), chainhash.HashSize) + } + + // R' = R-T (or R+T if it needs negation) + R := &sig.r // NOTE: R is an affine point + var RHat btcec.JacobianPoint + if sig.needNegation { + btcec.AddNonConst(R, T, &RHat) + } else { + btcec.AddNonConst(R, negatePoint(T), &RHat) + } + + RHat.ToAffine() + + // P = lift_x(int(pk)) + pubKey, err := schnorr.ParsePubKey(pubKeyBytes) + if err != nil { + return err + } + // Fail if P is not a point on the curve + if !pubKey.IsOnCurve() { + return fmt.Errorf("pubkey point is not on curve") + } + + // e = int(tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || M)) mod n. + var rBytes [chainhash.HashSize]byte + R.X.PutBytesUnchecked(rBytes[:]) + pBytes := schnorr.SerializePubKey(pubKey) + commitment := chainhash.TaggedHash( + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, m, + ) + var e btcec.ModNScalar + e.SetBytes((*[ModNScalarSize]byte)(commitment)) + + // Negate e here so we can use AddNonConst below to subtract the s'*G + // point from e*P. + e.Negate() + + // expected R' = s'*G - e*P + var P, expRHat, sHatG, eP btcec.JacobianPoint + pubKey.AsJacobian(&P) + btcec.ScalarBaseMultNonConst(&sig.sHat, &sHatG) // s'*G + btcec.ScalarMultNonConst(&e, &P, &eP) // -e*P + btcec.AddNonConst(&sHatG, &eP, &expRHat) // R' = s'*G-e*P + + // Fail if expected R' is the point at infinity + if (expRHat.X.IsZero() && expRHat.Y.IsZero()) || expRHat.Z.IsZero() { + return fmt.Errorf("expected R' point is at infinity") + } + + expRHat.ToAffine() + + // fail if expected R'.y is odd + if expRHat.Y.IsOdd() { + return fmt.Errorf("expected R'.y is odd") + } + + // ensure R' is same as the expected R' = s'*G - e*P + if !expRHat.X.Equals(&RHat.X) { + return fmt.Errorf("expected R' = s'*G - e*P is different from the actual R'") + } + + return nil +} + +// intoPointWithEvenY converts the given Jacobian point to an affine +// point with even y value, and returns a bool value on whether the +// negation is performed. +// The bool value will be used for decrypting an adaptor signature +// to a Schnorr signature. +func intoPointWithEvenY(point *btcec.JacobianPoint) (*btcec.JacobianPoint, bool) { + affinePoint := point + affinePoint.ToAffine() + + needNegation := affinePoint.Y.IsOdd() + + if needNegation { + affinePoint = negatePoint(affinePoint) + } + + return affinePoint, needNegation +} + +// negatePoint negates a point (either Jacobian or affine) +func negatePoint(point *btcec.JacobianPoint) *btcec.JacobianPoint { + nPoint := *point + nPoint.Y.Negate(1).Normalize() + return &nPoint +} + +// unpackSchnorrSig +func unpackSchnorrSig(sig *schnorr.Signature) (*btcec.FieldVal, *btcec.ModNScalar) { + sigBytes := sig.Serialize() + var r btcec.FieldVal + r.SetByteSlice(sigBytes[0:32]) + var s btcec.ModNScalar + s.SetByteSlice(sigBytes[32:64]) + return &r, &s +} diff --git a/docker-compose.yml b/docker-compose.yml index e864519c6..8b40cf1fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,7 +45,6 @@ services: container_name: babylondnode2 image: "babylonchain/babylond" environment: - - HOME=/data/node2/babylond - LOG=${LOG:-babylond.log} command: > babylond --home /babylondhome start --log_format 'plain' 2>&1 | tee /babylondhome/babylond.log @@ -68,7 +67,6 @@ services: container_name: babylondnode3 image: "babylonchain/babylond" environment: - - HOME=/data/node3/babylond - LOG=${LOG:-babylond.log} command: > babylond --home /babylondhome start --log_format 'plain' 2>&1 | tee /babylondhome/babylond.log diff --git a/docs/README.md b/docs/README.md index 4ac29268d..33e8d35e2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,3 +8,5 @@ For user-facing docs, visit the [Babylon documentation page](https://docs.babylo - [Development Requirements](./dev-reqs.md) - [Running a node for testing purposes](./run-node.md) +- [Babylon system architecture](./architecture.md) +- [Staking BTC script](./staking-script.md) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..0a98a4938 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,229 @@ +# Babylon Architecture + +The Babylon system is composed of a Babylon node +built using the Cosmos SDK as well as peripheral programs +that facilitate BTC staking, finality round participation, and +communication with Bitcoin and other Consumer Zones. +![Babylon Architecture](./static/arch.png) + +## Babylon Node Modules + +### [Epoching](../x/epoching) + +The Babylon blockchain is divided into epochs +that consist of a parameterized number of blocks. +Within each epoch, the validator set does not change. +This way, Babylon needs a checkpoint per epoch rather than per block, +which reduces the checkpointing costs. +The epoching module achieves this by delaying the execution +of transactions that affect the validator set to the last block +of each epoch. + +### [BTC Light Client](../x/btclightclient) + +The BTC Light Client module receives Bitcoin headers +reported by the Vigilante Reporter and +maintains a BTC header chain based on the PoW rules of Bitcoin. +It exposes information about the canonical Bitcoin chain, +the depth of headers, and +whether the inclusion evidence for a Bitcoin transaction is valid. + +### [BTC Checkpoint](../x/btccheckpoint) + +The BTC Checkpoint module verifies Babylon’s BTC checkpoints +reported by the Vigilante Reporter, and +provides the confirmation status of these checkpoints to the Checkpointing +module based on their depth according to the BTC Light Client module. + +### [Checkpointing](../x/checkpointing) + +The checkpointing module is responsible for creating Babylon checkpoints +to be submitted to Bitcoin and maintaining their confirmation status. +It collects the validator's +[BLS signatures](https://en.wikipedia.org/wiki/BLS_digital_signature) +for each block to be checkpointed and aggregates them +into a BLS multisignature to include in the Bitcoin checkpoint. +The confirmation status of each checkpoint is determined by +Bitcoin checkpoint inclusion information retrieved from the +BTC checkpoint module. + +### [ZoneConcierge](../x/zoneconcierge) + +The Zone Concierge module +extracts verified Consumer Zone headers from +connected [IBC light clients](https://github.com/cosmos/ibc-go) and +maintains their Bitcoin confirmation status based on the +Bitcoin confirmation status of the +Babylon transactions that carry them. +It communicates the Bitcoin confirmation status to the Consumer Zone +using verifiable proofs through an +[IBC](https://github.com/cosmos/ibc-go) connection. + +### [BTC Staking](../x/btcstaking) + +The BTC Staking module +is the bookkeeper for the BTC staking protocol. +It is responsible for verifying and activating +BTC staking requests and +maintaining the active finality provider set. +It communicates with the BTC Light Client module +to extract the confirmation status of staking requests and +receives notifications about on-demand unlocked stake from the +BTC Staking Monitor. + +### [Finality](../x/finality) + +The Finality module is responsible for finalizing blocks +produced by the CometBFT consensus. +It receives and verifies finality round votes +from finality providers and +a block is considered finalized if sufficient +voting power is cast on it. +The voting power of each finality provider is based on +its Bitcoin stake retrieved from the BTC Staking module. +Finality votes are performed using +[Extractable-One-Time-Signatures (EOTS)](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf) +and verified using +the finality providers' committed public randomness. + +### [Incentive](../x/incentive) + +The incentive module consumes a percentage +of the rewards intended for Babylon stakers and +distributes it as rewards to Bitcoin stakers and +vigilantes. + +## Vigilantes + +The vigilante suite of programs acts as a +relayer of data between Babylon and Bitcoin. +Babylon's secure operation requires +that at least one honest +operator of each of the programs exist. +Otherwise, +an alarm will be raised by the monitor program. + +### [Vigilante Submitter](https://github.com/babylonchain/vigilante) + +A standalone program that submits +Babylon checkpoints to Bitcoin as +Bitcoin transactions embedding data +utilising the `OP_RETURN` Bitcoin script code. + +### [Vigilante Reporter](https://github.com/babylonchain/vigilante) + +A standalone program that scans +the Bitcoin ledger for Bitcoin headers and Babylon checkpoints, +and reports them back to Babylon using Babylon transactions. + +## Monitors + +The monitor programs suite is responsible for +monitoring the consistency between Babylon's state and +Bitcoin. + +### [Checkpointing Monitor](https://github.com/babylonchain/vigilante) + +A standalone program that monitors: + +- The consistency between the Bitcoin canonical chain and + the Bitcoin header chain maintained by + Babylon's BTC Light client module. +- The timely inclusion of Babylon's Bitcoin checkpoints + information in the Babylon ledger. + +### [BTC Staking Monitor](https://github.com/babylonchain/vigilante) + +A standalone program that monitors: + +- The execution of BTC Staking on-demand unbonding transactions + on the Bitcoin ledger to inform Babylon about them. +- The execution of BTC Staking slashing transactions in the case + of a finality provider double voting. + In the case of non-execution the monitor extracts the finality provider's + private key and executes the slashing. +- The execution of a selective slashing attack launched + by a finality provider. In this case, + the monitor extracts the finality provider's private key + and slashes them. + +## BTC Staking Programs + +The BTC Staking programs suite +involves components that enable the function +Bitcoin Stakers and Finality Providers +while also ensuring their adherence to the protocol. + +### BTC Staker + +Bitcoin holders can stake their Bitcoin +by creating a set of Bitcoin transactions, +including them to the Bitcoin ledger, and +then informing Babylon about their staking. +Later, they can also on-demand unlock or +withdraw their funds when their stake expires. +The following set of standalone programs +has been developed to enable these functionalities: + +- [BTC Staker Daemon](https://github.com/babylonchain/btc-staker): + Daemon program connecting to a Bitcoin wallet and Babylon. +- [BTC Staker Dashboard](https://github.com/babylonchain/btc-staking-dashboard): + Web application connecting to a Bitcoin wallet extension and the Babylon API. + Should only be used for testing purposes. +- Wallet Integrations (TBD) + +### [Finality Provider](https://github.com/babylonchain/finality-provider) + +A standalone program that allows for the registration and +maintenance of a finality provider. +It monitors for a finality provider's inclusion in the active set, commits +[Extractable One Time Signature (EOTS)](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf) +public randomness, and +submits finality votes for blocks. +Finality votes are created through a connection to a standalone +[EOTS manager daemon](https://github.com/babylonchain/finality-provider) +responsible for securely maintaining the +finality provider's private keys. + +### [Covenant Emulator](https://github.com/babylonchain/covenant-emulator) + +A standalone program utilised by the covenant emulation committee members. +It emulates [covenant](https://covenants.info) functionality by monitoring +for pending staking requests, +verifying their contents, and +submitting necessary signatures. + +## Consumer Zones + +### IBC Relayer + +The IBC Relayer maintains the +[IBC protocol](https://cosmos.network/ibc/) connection +between Babylon and other Consumer Zones (CZs). +It is responsible for updating the CZ's light client +inside the Babylon ledger to enable checkpointing and +propagating checkpoint information to the Babylon smart contract +deployed within the CZ. + +There are different IBC relayer implementations that can achieve +this function. Most notably: + +- [Cosmos Relayer](https://github.com/cosmos/relayer): + A fully functional relayer written in Go. +- [Babylon Relayer](https://github.com/babylonchain/babylon-relayer/): + A wrapper of the Cosmos Relayer that can maintain a one-way IBC connection. + It is recommended to be used when the Consumer Zone does not deploy the + Babylon smart contract. +- [Hermes Relayer](https://github.com/informalsystems/hermes): + A fully functional relayer written in Rust. + +### [Babylon Contract](https://github.com/babylonchain/babylon-contract) + +A [CosmWasm](https://cosmwasm.com/) smart contract intended for +deployment in a Consumer Zone. +It enables Bitcoin Checkpointing functionality without introducing +invasive changes in the codebase of the Consumer Zone. +Based on the Bitcoin Checkpointing functionality, +the Consumer Zone can make decisions based on the inclusion +of its checkpoints in the Bitcoin ledger +(e.g. execute BTC-assisted unbonding requests). diff --git a/docs/staking-script.md b/docs/staking-script.md new file mode 100644 index 000000000..41003e6b0 --- /dev/null +++ b/docs/staking-script.md @@ -0,0 +1,298 @@ +# Staking BTC script + +## Introduction + +Babylon's BTC staking protocol turns Bitcoin into a staking asset with the aim +to enhance the economic security of the Babylon chain. Bitcoin holders can stake +their Bitcoin by locking them using a special transaction on the Bitcoin chain. +The locked Bitcoin contribute to Babylon chain's economic security and generate +yields for the Bitcoin stakers. +The protocol has the following important properties: + +1. The staking is trustless and self-custodial, meaning it does not require + bridging, wrapping, 3rd-party custody/multi-sig, or oracle. +2. The stake voting power is delegatable, a common feature of Delegated PoS + (DPoS) protocols. +3. Stake security: the staked bitcoin is slashed if and only if the BTC staker + itself (and in case of delegation, its delegatee) is malicious. This implies + that, even if the Babylon chain is compromised and taken over by malicious + actors, as long as the BTC staker itself (and the validator it delegates to + in case of delegation) is not malicious, its bitcoin stake is secure. +4. The protocol supports fractional slashing, meaning the protocol can be + configured so that when slashing happens, only a fraction of the staked + bitcoins are slashed, and the rest is returned to the staker. +5. The protocol allows stakers to on-demand unbond, On-demand unbonding, means + the BTC staker can withdraw from staking with an unbonding delay. +6. Atomic slashing, meaning that if a delegator is slashed, then all the + delegators of the same delegatee, including the delegatee's self-delegation, + will be subject to slashing. In other words, the delegatee cannot + selectively harm any specific delegator. +7. (WIP) restakable, meaning that the same bitcoin can be staked to secure + multiple PoS chains and earn multiple PoS yields + +The key to making all these possible is special constructions of BTC +transactions using BTC scripts. + +## Preliminary + +Babylon interaction with Bitcoin is heavily based on Bitcoin's +[Taproot upgrade](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawik). +This design choice was made due to the heavy usage of +[Schnorr signatures](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) +that were introduced through the Taproot upgrade. + +## Staking Flow + +Following diagram show how different transactions described in following +paragraphs create and spend different bitcoin outputs: + +```mermaid +stateDiagram-v2 + active: Staking output + unbonding: Unbonding output + state stake_slashing <> + state unbonding_slashing <> + burn_staking: Burn address output + change_staking: Change output + burn_unbonding: Burn address output + change_unbonding: Change output + + [*] --> active: Staking transaction + + active --> unbonding: Unbonding transaction + + unbonding --> [*]: Unbonding withdrawal transaction + + unbonding --> unbonding_slashing: Slashing transaction + unbonding_slashing --> burn_unbonding + unbonding_slashing --> change_unbonding + change_unbonding --> [*]: Withdrawal transaction + + active --> [*]: Staking withdrawal transaction + + active --> stake_slashing: Slashing transaction + + stake_slashing --> burn_staking + stake_slashing --> change_staking + change_staking --> [*]: Withdrawal transaction +``` + +Withdrawal transactions are btc transactions which transfer bitcoins to the btc +staker wallet. + +## Types of special transactions + +There are three special transaction types recognized by the Babylon chain: + +- Staking Transaction +- Unbonding Transaction +- Slashing Transaction + +### Staking Transaction + +A BTC holder gains voting power by creating a staking transaction. This is a +Bitcoin transaction that commits a certain amount of to-be-staked bitcoin to +Babylon recognised BTC staking scripts. These scripts lock the stake for a +chosen amount of BTC blocks and enable other features such as unbonding and +slashable safety. + +The requirements for a valid staking transaction are: + +- it can contain arbitrary number of inputs +- it can contain arbitrary number of outputs. One of those outputs must be +a Taproot output committing to the BTC staking scripts recognized by Babylon. +Henceforth known as `Staking Output`. + +### Unbonding Transaction + +The BTC staker utilises the unbonding transaction when they want to unlock +their stake before their originally committed timelock has expired. + +The requirements for a valid unbonding transaction are: + +- it contains exactly one input which points to staking transaction's `Staking + Output`. +- it contains exactly one output which must be a Taproot output committing to +the BTC unbonding scripts recognized by Babylon. Henceforth known as `Unbonding +Output`. + +### Slashing Transaction + +The slashing transaction is used to punish a BTC staker when they (or the +finality provider they have delegated to) perform an offense. + +The requirements for a valid slashing transaction are: + +- it must have exactly one input pointing to either the staking output or the + unbonding output +- it must have exactly two outputs, the first sending the slashed fraction of +the funds to a burn address specified in the Babylon chain's parameters and the +second sending the remaining funds back to the BTC staker's address. +- the fee for the slashing transactions must be larger than or equal to the + minimal fee specified in Babylon's parameters + +## Staking and Unbonding output scripts + +### Staking output + +The staking output is a taproot output which can only be spent through a script +spending path. +The key spending path is disabled by using the "Nothing Up My Sleeve" +(NUMS) point as internal key. Chosen point is the one described in +[BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs) +i.e, + +``` +H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0) +``` + +which is a point constructed by taking the hash of the standard uncompressed +encoding of the secp256k1 base point `G` as the X coordinate. + +The staking output can be spent by three script spending paths. + +#### 1. Timelock path + +The timelock path locks the staker's Bitcoin for a pre-determined number of +Bitcoin blocks. It commits to a script of the form: + +``` + OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY +``` + +where: + +- `` is the BTC staker's public key.. +- `` is the lockup period denoted in Bitcoin blocks. The +timelock comes into effect after the Bitcoin transaction has been included in a +mined block. In essence, the script denotes that only the staker can unlock the +funds after the timelock has passed. It must be lower than `65535`. + +#### 2. Unbonding path + +The unbonding path allows the staker to on-demand unlock their locked Bitcoin +before the timelock expires. It commits to a script of the form: + +``` + OP_CHECKSIGVERIFY + OP_CHECKSIGADD OP_CHECKSIGADD ... OP_CHECKSIGADD + OP_GREATERTHANOREQUAL +``` + +where: + +- `Staker_PK` is the BTC staker's public key +- `CovenantPk1..CovenantPkN` are the lexicographically sorted public keys of the +current covenant committee recognized by the Babylon chain +- `CovenantThreshold` is a Babylon parameter specifying the number of how many +covenant committee member signatures are required. + +Signatures from a quorum of the covenant committee are required to ensure that +this script is not used for on-demand unlocking without the stake going through +an unbonding period. reward all covenant members for their work. + +#### 3. Slashing path + +The slashing path is utilised for punishing finality providers and their +delegators in the case of double signing. It commits to a script: + +``` + OP_CHECKSIGVERIFY + OP_CHECKSIGVERIFY + OP_CHECKSIGADD OP_CHECKSIGADD ... OP_CHECKSIGADD + OP_GREATERTHANOREQUAL +``` + +where: + +- `StakerPK` is the BTC staker's public key +- `FinalityProviderPk` is the BTC public key of the finality provider to which + the staker delegates their stake +- `CovenantPk1..CovenantPkN` are the lexicographically sorted public keys of the +current covenant committee members recognized by the Babylon chain +- `CovenantThreshold` is a Babylon parameter denoting how many covenant +committee member signatures are required. + +This path can only be executed with the collaboration of the BTC staker, +finality provider, and covenant committee. +It is used in following way: + +- for stake to become active, staker must publish pre signed slashing transaction +- covenant committee validates such transaction, and publish its own signatures. +- the only signature missing to send slashing transaction is finality provider +signature. If finality provider private key leaks due to infractions, anyone can +sign slashing transaction and send slashing transaction to Bitcoin network. + +#### Difference between Unbonding and Slashing Path + +The main difference between the unbonding and slashing paths is the existence of +`FinalityProviderPk` in the slashing path. + +This leads to following system wide repercussions: + +- for staking request to become active, btc holder needs to provide valid +unbonding transaction in this staking request. This staking request will become +active only when `CovenantThreshold` signatures will be received by Babylon +chain. Lack of `FinalityProviderPk` in unbonding path, means that after +delegation becomes active, staker can send unbodning transaction any time +without asking finality provider for permission. +- existence of `FinalityProviderPk` in slashing path, coupled with the fact that +btc holder needs to provide pre-signed slashing transaction which needs to be +signed by covenant committee for delegation request to become active, leads to +situation in which the only signature missing to send slashing transaction to +btc is signature of finality provider. + +### Unbonding output + +Unbonding output is a taproot output which can be only spent through script +spending path. The key spending path is disabled by using "Nothing Up My Sleeve" +(NUMS) point as internal key. Chosen point is the one described in BIP341 i.e H += lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0) +which is point constructed by taking the hash of the standard uncompressed +encoding of the secp256k1 base point G as X coordinate. + +Unbonding output can be spent by two script spending paths. + +#### 1. Timelock path + +The timelock path locks the staker's Bitcoin for a pre-determined number of +Bitcoin blocks. It commits to a script of the form: + +``` + OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY` +``` + +where: + +- Staker_PK is btc holder public key +- Timelock_Blocks is unbonding time. It must be lower or equal 65535, but larger +than `max(MinUnbondingTime, CheckpointFinalizationTimeout)`. `MinUnbondingTime` +and `CheckpointFinalizationTimeout` are Babylon parameters. + +#### 2. Slashing path + +The slashing path is utilised for punishing finality providers and their +delegators in the case of double signing. It commits to a script: + +``` + OP_CHECKSIGVERIFY + OP_CHECKSIGVERIFY + OP_CHECKSIGADD OP_CHECKSIGADD ... OP_CHECKSIGADD + OP_GREATERTHANOREQUAL +``` + +where: + +- `StakerPK` is the BTC staker's public key +- `FinalityProviderPk` is the BTC public key of the finality provider to which + the staker delegates their stake +- `CovenantPk1..CovenantPkN` are the lexicographically sorted public keys of the +current covenant committee members recognized by the Babylon chain +- `CovenantThreshold` is a Babylon parameter denoting how many covenant +committee member signatures are required. + +#### Existence of Slashing path in Unbonding output + +The fact that slashing path exists in unbonding output means that even if staker +is unbonding he can be slashed if finality provider commits infraction during +unbonding time. diff --git a/docs/static/arch.excalidraw b/docs/static/arch.excalidraw new file mode 100644 index 000000000..bbd73cd87 --- /dev/null +++ b/docs/static/arch.excalidraw @@ -0,0 +1,12630 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 340, + "versionNonce": 1771279469, + "isDeleted": false, + "id": "mcq3uHl3gntSG5245aRvO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10581.623910435768, + "y": 1514.456137017838, + "strokeColor": "#1e1e1e", + "backgroundColor": "#eebefa", + "width": 3474.2757184114666, + "height": 1162.4597560067823, + "seed": 1952395142, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "PIo2X8_qG0ipchEme9mOU", + "type": "arrow" + }, + { + "id": "OMRiiKcWvoZZ9P2ZIgA2p", + "type": "arrow" + }, + { + "id": "7hrt9Q8ALp2hgi-CH7RhI", + "type": "arrow" + }, + { + "id": "BWjQ8wJWdXaye_ajaXVvj", + "type": "arrow" + }, + { + "id": "kpT676I1UQJTahGMjR2KY", + "type": "arrow" + }, + { + "id": "8E8bIA1TLsDkEkmLGooFY", + "type": "arrow" + }, + { + "id": "GFH7lVRLTqVlZnVWPiljy", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 407, + "versionNonce": 1517286627, + "isDeleted": false, + "id": "EK_xusAsckpkIcS-MXJEd", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10598.136996979245, + "y": 1553.7018200231457, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 3434.6746007716406, + "height": 529.3673460007709, + "seed": 576342406, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "YJq6laSN-yx57jGCvqtHl", + "type": "arrow" + }, + { + "id": "i8rUSngD92Yf-8G5kqyGx", + "type": "arrow" + }, + { + "id": "Th72X_O0Jcw9QslRrN6dG", + "type": "arrow" + }, + { + "id": "ubD9PQiYw8EIHbYNeDuZg", + "type": "arrow" + }, + { + "id": "u56fJvEUflV3M4Wf2lfxj", + "type": "arrow" + }, + { + "id": "XfoF5gyizY9lr0o_lqvKd", + "type": "arrow" + }, + { + "id": "4C_kRR9PuZj7jJ5ntwVfz", + "type": "arrow" + }, + { + "id": "ByxqHImFIZUnMJs-EiM9L", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 375, + "versionNonce": 1012956877, + "isDeleted": false, + "id": "jI5SxjH5n3FRdYSSMZzjk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935.254278524366, + "y": 810.9461934446426, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 709.0507674670008, + "height": 1438.7687038603308, + "seed": 214421556, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "K1_BQ-eNxQoevuNfEo-FQ", + "type": "arrow" + }, + { + "id": "BNsbfOJxpu3SiumhnV4Pp", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 245, + "versionNonce": 386228355, + "isDeleted": false, + "id": "igFd0InbawEl8svohKYrJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1063.448970961376, + "y": 838.3187252660047, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 197.39999389648438, + "height": 67.76958097087783, + "seed": 1231820212, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 54.21566477670226, + "fontFamily": 1, + "text": "Babylon", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon", + "lineHeight": 1.25, + "baseline": 48 + }, + { + "type": "line", + "version": 1581, + "versionNonce": 1323375917, + "isDeleted": false, + "id": "9eBByjO2HfoGPqFBH7hwX", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1131.836454370302, + "y": 2565.185456674697, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1046945744, + "groupIds": [ + "bQLU23_-t1X52UFFmjbDe", + "kaUp0V4XDOFsPbtazd_zf" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1260, + "versionNonce": 1630296099, + "isDeleted": false, + "id": "zpoa8O-C_49AKFWWUkuEO", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1138.5140601484704, + "y": 2529.42657239686, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 437429712, + "groupIds": [ + "bQLU23_-t1X52UFFmjbDe", + "kaUp0V4XDOFsPbtazd_zf" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1558, + "versionNonce": 1484713869, + "isDeleted": false, + "id": "6YnAv5R0wSRRQHrxVkGVA", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1203.6893504013765, + "y": 2561.763890197028, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1097861584, + "groupIds": [ + "L841L7GshHE7nhetcYpu9", + "79DD2RffIvBe48C8kMKLV" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1237, + "versionNonce": 651385795, + "isDeleted": false, + "id": "bDrow_QQ2uoMWyAkhmLt9", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1210.366956179545, + "y": 2526.005005919191, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1617842128, + "groupIds": [ + "L841L7GshHE7nhetcYpu9", + "79DD2RffIvBe48C8kMKLV" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1563, + "versionNonce": 1735620077, + "isDeleted": false, + "id": "p_UMN7oDs17jo84NCppRZ", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1272.120679954781, + "y": 2563.474673435862, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1073423152, + "groupIds": [ + "FVJgsTSbWaBLiHWr0J8rR", + "buYNEMfFx4pfCg3Jwxhoo" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1242, + "versionNonce": 337177443, + "isDeleted": false, + "id": "ZND1YX1Z3a9r3CApWkd_K", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1278.7982857329496, + "y": 2527.715789158025, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 84884784, + "groupIds": [ + "FVJgsTSbWaBLiHWr0J8rR", + "buYNEMfFx4pfCg3Jwxhoo" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 472, + "versionNonce": 113782861, + "isDeleted": false, + "id": "X9v7_TtCdmuXMD42T1sms", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 989.9993421670897, + "y": 2439.6118368156713, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 475.5977403961617, + "height": 155.681274733995, + "seed": 1568945104, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9-neyMzbXyIAzVdCZgE37", + "type": "arrow" + }, + { + "id": "hIGhfc7N5UnvINvkJx3X-", + "type": "arrow" + }, + { + "id": "hqjhvGv_tzZo3qWQSfyYg", + "type": "arrow" + }, + { + "id": "3EYM65srfkkFAkRf4Nf0R", + "type": "arrow" + }, + { + "id": "h-FxVGkngS5rHxj52BuW6", + "type": "arrow" + }, + { + "id": "I7ZDOOFXqDv-baF94yeo3", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 166, + "versionNonce": 1296795395, + "isDeleted": false, + "id": "QbINWPdkTxErSSSZcflzt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1087.5139867806913, + "y": 2448.430452442857, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 276.0333251953125, + "height": 45, + "seed": 1255878960, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC Validators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Validators", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 1614, + "versionNonce": 609656493, + "isDeleted": false, + "id": "9-neyMzbXyIAzVdCZgE37", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 985.737144751741, + "y": 2439.7865955042416, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 580.3638685513667, + "height": 820.1585715411788, + "seed": 1266209232, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "tiAtyPBHjgSuF9M_WHV7m" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": -0.5053529283640728, + "gap": 4.2621974153486235 + }, + "endBinding": { + "elementId": "oWuiUx2ktjtNyWW0RbdKk", + "focus": 0.24670147912627394, + "gap": 9.85634884674755 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -529.5021731012084, + -508.27738062259823 + ], + [ + 50.86169545015832, + -820.1585715411788 + ] + ] + }, + { + "type": "text", + "version": 104, + "versionNonce": 1760145059, + "isDeleted": false, + "id": "tiAtyPBHjgSuF9M_WHV7m", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 974.5056998931386, + "y": 2018.234642734079, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 80.5999984741211, + "height": 25, + "seed": 186621904, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Register", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "9-neyMzbXyIAzVdCZgE37", + "originalText": "Register", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 485, + "versionNonce": 919093517, + "isDeleted": false, + "id": "l-VxcloF1H5GLPDnWvCWJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 298.8429136777026, + "y": 1418.2742432311086, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 234.37730372041068, + "height": 278.85766793012385, + "seed": 1239093200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "K1_BQ-eNxQoevuNfEo-FQ", + "type": "arrow" + }, + { + "id": "BNsbfOJxpu3SiumhnV4Pp", + "type": "arrow" + }, + { + "id": "EowCRh9q6NgH7YHsP82Ls", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 244, + "versionNonce": 698475075, + "isDeleted": false, + "id": "5f26CvXqEUV7O-SBwo0Cv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 319.3723125437237, + "y": 1439.06834153014, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 194.23333740234375, + "height": 45, + "seed": 2015116592, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Delegators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Delegators", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "line", + "version": 1568, + "versionNonce": 597071725, + "isDeleted": false, + "id": "omrSW1p9HQfSxC3CFXXky", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 396.1996616712015, + "y": 1562.6664787173202, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 253368272, + "groupIds": [ + "73fwTXN88iALHhbYutpmU", + "CDyz2Gb40BrRst5hBlNBj" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1248, + "versionNonce": 2018681315, + "isDeleted": false, + "id": "Hm2H_LnAkLQPiDu-55X5B", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 402.87726744937004, + "y": 1526.9075944394833, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1235656144, + "groupIds": [ + "73fwTXN88iALHhbYutpmU", + "CDyz2Gb40BrRst5hBlNBj" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1518, + "versionNonce": 1695228365, + "isDeleted": false, + "id": "7hi9JyWZNGZ9tZ-V9gDzY", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 392.77809519353127, + "y": 1624.254675315385, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 57334576, + "groupIds": [ + "pGByMNfvcC_QP1hHr0pvw", + "9faYOwl283k8XB1RgmZxT" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1197, + "versionNonce": 503999875, + "isDeleted": false, + "id": "h-RqYNgfETQnnNArxRuQk", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 399.4557009716998, + "y": 1588.4957910375476, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 835188016, + "groupIds": [ + "pGByMNfvcC_QP1hHr0pvw", + "9faYOwl283k8XB1RgmZxT" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1520, + "versionNonce": 1883201581, + "isDeleted": false, + "id": "yTfBBenj07FGW3lNyp2Gr", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 394.4888784323665, + "y": 1680.7105221969434, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1604977104, + "groupIds": [ + "xf8IqsZHP5ZflMZVLMDIB", + "GVI2jR5M9qW-gM-aZdViG" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1200, + "versionNonce": 1040696611, + "isDeleted": false, + "id": "E8ta0We_g-r_JrmBOAf8l", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 401.16648421053503, + "y": 1644.9516379191061, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 346638288, + "groupIds": [ + "xf8IqsZHP5ZflMZVLMDIB", + "GVI2jR5M9qW-gM-aZdViG" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 2053, + "versionNonce": 1528615565, + "isDeleted": false, + "id": "K1_BQ-eNxQoevuNfEo-FQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 536.6417838757835, + "y": 1522.760565187596, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 499.54870573985306, + "height": 72.19291385590486, + "seed": 352565200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "nAJhEtoz2L8Nz6Uo4K3IL" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "l-VxcloF1H5GLPDnWvCWJ", + "focus": -0.07803258904145931, + "gap": 3.421566477670268 + }, + "endBinding": { + "elementId": "oWuiUx2ktjtNyWW0RbdKk", + "focus": -0.25279303227658806, + "gap": 10.264699433010264 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.7743528699259, + -46.31969183609317 + ], + [ + 499.54870573985306, + 25.873222019811692 + ] + ] + }, + { + "type": "text", + "version": 82, + "versionNonce": 263792835, + "isDeleted": false, + "id": "nAJhEtoz2L8Nz6Uo4K3IL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652.6318355255535, + "y": 1569.1540425398616, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 219.6666717529297, + "height": 25, + "seed": 1108101936, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Delegate to Validator", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "K1_BQ-eNxQoevuNfEo-FQ", + "originalText": "Delegate to Validator", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 1966, + "versionNonce": 1113389293, + "isDeleted": false, + "id": "BNsbfOJxpu3SiumhnV4Pp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1034.4797063768012, + "y": 1578.5723840967194, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 492.70557278451236, + "height": 47.04080725299514, + "seed": 1920390448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "c0unLHK01D1YMERb5apNM" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "oWuiUx2ktjtNyWW0RbdKk", + "focus": -0.007235549823675875, + "gap": 11.975482671845612 + }, + "endBinding": { + "elementId": "l-VxcloF1H5GLPDnWvCWJ", + "focus": -0.210158554590088, + "gap": 8.553916194175628 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -242.93121991458645, + 15.91253273440634 + ], + [ + -492.70557278451236, + -31.128274518588796 + ] + ] + }, + { + "type": "text", + "version": 127, + "versionNonce": 1488787555, + "isDeleted": false, + "id": "c0unLHK01D1YMERb5apNM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 698.2592905173183, + "y": 1610.874588854431, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 131.8333282470703, + "height": 75, + "seed": 827577296, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Validators\nand chains\nthey validate", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BNsbfOJxpu3SiumhnV4Pp", + "originalText": "Validators\nand chains\nthey validate", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "rectangle", + "version": 334, + "versionNonce": 1871270733, + "isDeleted": false, + "id": "3Kg-UpYyQzQOkeMXdE-kQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1046.4551890486468, + "y": 2040.99934216709, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 396.90171140974655, + "height": 201.87242218254346, + "seed": 1167215056, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "ve_qswif0oRQu7buEaV8r", + "type": "arrow" + }, + { + "id": "hqjhvGv_tzZo3qWQSfyYg", + "type": "arrow" + }, + { + "id": "3EYM65srfkkFAkRf4Nf0R", + "type": "arrow" + }, + { + "id": "CLmpSDPqzoyNVdM8bskrx", + "type": "arrow" + }, + { + "id": "fHa7mU34mFfK6rFMd8b8W", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 518, + "versionNonce": 652350467, + "isDeleted": false, + "id": "Dy0E3pNJ_DML2Soyo5PZK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1958.3026553477625, + "y": 812.6569766834787, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 656.9407637126835, + "height": 1455.8765362486815, + "seed": 96971056, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 150, + "versionNonce": 1056661933, + "isDeleted": false, + "id": "fhfk9Qc8nhOCy-NKkk7Os", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2103.719230648747, + "y": 835.1618582213459, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 265.5666809082031, + "height": 45, + "seed": 251421488, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Consumer Chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Consumer Chain", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 342, + "versionNonce": 1244044195, + "isDeleted": false, + "id": "XRsX4CQXeYyrF6iFbcwkf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1990.8075368856294, + "y": 927.2794536854315, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 586.7986509204438, + "height": 1086.347356660297, + "seed": 1025845712, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "RjZMbOE6EaUXBfBevc8RA", + "type": "arrow" + }, + { + "id": "k5XmRkToqNRcQ9CVHvwF2", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 156, + "versionNonce": 1172087821, + "isDeleted": false, + "id": "jp53mA0XDBTCeudMNmCdu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2019.8908519458262, + "y": 937.4118034019367, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 195.8000030517578, + "height": 35, + "seed": 1684464592, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BBN Contract", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BBN Contract", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 167, + "versionNonce": 247017283, + "isDeleted": false, + "id": "-fU10jT3GNlbPP5PigmFn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.9257901826256, + "y": 992.2892167611656, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 434.53894266411885, + "height": 102.64699433010647, + "seed": 1793259824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "aPy186FNWzxJXzWqaVRUa", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 107, + "versionNonce": 942053997, + "isDeleted": false, + "id": "8oUpk-NBK6jhtFsVmT0l-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1116.5973018408865, + "y": 1011.2401821048572, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 226.46665954589844, + "height": 45, + "seed": 551498544, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Timestamping", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Timestamping", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 206, + "versionNonce": 922242787, + "isDeleted": false, + "id": "IE6raU1vN7ZaQgjKbsP_I", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2040.420250811848, + "y": 1000.8431329553414, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 506.3918386951934, + "height": 94.09307813593159, + "seed": 2124307248, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "sJTA7xjvulEX_PgGCpiTw", + "type": "arrow" + }, + { + "id": "aPy186FNWzxJXzWqaVRUa", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 167, + "versionNonce": 245548237, + "isDeleted": false, + "id": "S3TVaWOLg26PaZPRq9iFV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.278007089795098, + "x": 2149.910378097295, + "y": 1019.9264480155383, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 226.46665954589844, + "height": 45, + "seed": 1514290640, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Timestamping", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Timestamping", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 245, + "versionNonce": 896694915, + "isDeleted": false, + "id": "oXlZjJ6fHpM0JEGCEuxaN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2045.5526005283534, + "y": 1242.0635696310922, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 496.1271392621827, + "height": 179.6322400776869, + "seed": 1366099248, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "sJTA7xjvulEX_PgGCpiTw", + "type": "arrow" + }, + { + "id": "bdiHkTDzy9zV8N9HxCSSw", + "type": "arrow" + }, + { + "id": "tzgh7GO8Oh0n7_ZVppRoK", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 193, + "versionNonce": 1827316525, + "isDeleted": false, + "id": "LQevBNiNcVk_XVP9ywkkY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2177.282909918658, + "y": 1267.7253182136192, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 180.8333282470703, + "height": 45, + "seed": 1205069104, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "btcstaking", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "btcstaking", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 347, + "versionNonce": 652620323, + "isDeleted": false, + "id": "uzA8ziWTtK0AsmUy3zLry", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2093.454531215736, + "y": 1342.7350812893528, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 415.6333312988281, + "height": 50, + "seed": 1095966672, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- Active BTC validators and delegations\n- Verifies delegations based on BTC view ", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Active BTC validators and delegations\n- Verifies delegations based on BTC view ", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 155, + "versionNonce": 1197979021, + "isDeleted": false, + "id": "w2dXDneqDY3sHfMyjPTXG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2332.9641846526524, + "y": 2135.0924203030218, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 222.40182104856444, + "height": 99.2254278524365, + "seed": 1662100944, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9nV-VJmIHd59YQ3cj9FmB" + }, + { + "id": "RjZMbOE6EaUXBfBevc8RA", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 126, + "versionNonce": 205649347, + "isDeleted": false, + "id": "9nV-VJmIHd59YQ3cj9FmB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2356.4317654039855, + "y": 2162.2051342292402, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 175.46665954589844, + "height": 45, + "seed": 106403280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Consensus", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "w2dXDneqDY3sHfMyjPTXG", + "originalText": "Consensus", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 910, + "versionNonce": 1230207981, + "isDeleted": false, + "id": "RjZMbOE6EaUXBfBevc8RA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2473.6172046407873, + "y": 2126.5385041088457, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.5520033377179, + "height": 464.4821305286473, + "seed": 2018388432, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "oraHyp86WSPqJCcz8KU3X" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "w2dXDneqDY3sHfMyjPTXG", + "focus": -0.04958680170408755, + "gap": 8.553916194175827 + }, + "endBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "focus": -0.8187827599837586, + "gap": 8.553916194174917 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 182.5520033377179, + -290.83315060196924 + ], + [ + 66.35175191091275, + -464.4821305286473 + ] + ] + }, + { + "type": "text", + "version": 67, + "versionNonce": 1151616355, + "isDeleted": false, + "id": "oraHyp86WSPqJCcz8KU3X", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2067.8368809322415, + "y": 1853.9994518059088, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 61.5, + "height": 25, + "seed": 749724624, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Blocks", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "RjZMbOE6EaUXBfBevc8RA", + "originalText": "Blocks", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 237, + "versionNonce": 2058843725, + "isDeleted": false, + "id": "MpZJWSpMDFY-n7qtelImN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2011.3369357516508, + "y": 2136.803203541857, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 234.37730372041096, + "height": 100, + "seed": 175513392, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "TiYDbxHw6Jvhwd-po9zCn" + }, + { + "id": "oxbKMbcxmmyRC0Uszo0za", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 202, + "versionNonce": 1197187331, + "isDeleted": false, + "id": "TiYDbxHw6Jvhwd-po9zCn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2035.1589189106844, + "y": 2141.803203541857, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 186.73333740234375, + "height": 90, + "seed": 1164512560, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Rewards \nMiddleware", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MpZJWSpMDFY-n7qtelImN", + "originalText": "Rewards Middleware", + "lineHeight": 1.25, + "baseline": 77 + }, + { + "type": "arrow", + "version": 1013, + "versionNonce": 644687021, + "isDeleted": false, + "id": "sJTA7xjvulEX_PgGCpiTw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2278.504308338577, + "y": 1103.4901272854481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.2717844870212502, + "height": 133.4410926291382, + "seed": 204340016, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "-Ezb4MpSw2xaKA8RhVKO0" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "IE6raU1vN7ZaQgjKbsP_I", + "focus": 0.054054055149846666, + "gap": 8.553916194175144 + }, + "endBinding": { + "elementId": "oXlZjJ6fHpM0JEGCEuxaN", + "focus": -0.08275862068965532, + "gap": 5.132349716505701 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.2717844870212502, + 133.4410926291382 + ] + ] + }, + { + "type": "text", + "version": 84, + "versionNonce": 501647523, + "isDeleted": false, + "id": "-Ezb4MpSw2xaKA8RhVKO0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2183.7850802186017, + "y": 1135.2106736000173, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 186.1666717529297, + "height": 70, + "seed": 1800789808, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BTC chain + \nCheckpoints", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "sJTA7xjvulEX_PgGCpiTw", + "originalText": "BTC chain + Checkpoints", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "rectangle", + "version": 233, + "versionNonce": 1967885, + "isDeleted": false, + "id": "bapEN-Oda9yJXFVTnxLpI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2060.9496496778693, + "y": 1570.5339514874343, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 470.46539067965614, + "height": 165.94597416700594, + "seed": 1044380624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "RjZMbOE6EaUXBfBevc8RA", + "type": "arrow" + }, + { + "id": "bdiHkTDzy9zV8N9HxCSSw", + "type": "arrow" + }, + { + "id": "h-FxVGkngS5rHxj52BuW6", + "type": "arrow" + }, + { + "id": "I7ZDOOFXqDv-baF94yeo3", + "type": "arrow" + }, + { + "id": "1mR6xqwOtINXSyrvyV3k2", + "type": "arrow" + }, + { + "id": "UW2vwnBaiRHvonL7pl21d", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 151, + "versionNonce": 502496323, + "isDeleted": false, + "id": "GEPC9a12TjZGnRUzicvlN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2220.052490889535, + "y": 1589.4849168311252, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 120.80000305175781, + "height": 45, + "seed": 1396162352, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "bdiHkTDzy9zV8N9HxCSSw", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "finality", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "finality", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 223, + "versionNonce": 172203373, + "isDeleted": false, + "id": "hw6VSdw7cEbOR96Bujjjr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2102.0084474099117, + "y": 1645.5437145631681, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 393.8333435058594, + "height": 75, + "seed": 1417770960, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- Collects consensus blocks\n- processes signatures for those blocks\n- manages validators' randomness", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Collects consensus blocks\n- processes signatures for those blocks\n- manages validators' randomness", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "arrow", + "version": 528, + "versionNonce": 1667300323, + "isDeleted": false, + "id": "bdiHkTDzy9zV8N9HxCSSw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2264.4835799034536, + "y": 1431.96050914179, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.6321014821578501, + "height": 130.01952615146865, + "seed": 2123662800, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "7Ft2TboD60hs2SxZu3iKG" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "oXlZjJ6fHpM0JEGCEuxaN", + "gap": 10.26469943301106, + "focus": 0.12196884172500101 + }, + "endBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "gap": 8.553916194175486, + "focus": -0.12239062053188139 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.6321014821578501, + 130.01952615146865 + ] + ] + }, + { + "type": "text", + "version": 114, + "versionNonce": 1009215437, + "isDeleted": false, + "id": "7Ft2TboD60hs2SxZu3iKG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2169.6103070392546, + "y": 1530.136902337918, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 193.26666259765625, + "height": 50, + "seed": 113373648, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Active Delegations \nfor block", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bdiHkTDzy9zV8N9HxCSSw", + "originalText": "Active Delegations for block", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 123, + "versionNonce": 171804547, + "isDeleted": false, + "id": "XLV0apwvUf-95Ikn5tQNT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2069.5035658720453, + "y": 1838.4537760189055, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 460.2006912466453, + "height": 143.7057920621496, + "seed": 92739376, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "oxbKMbcxmmyRC0Uszo0za", + "type": "arrow" + }, + { + "id": "FcQpoNogq2Nm9AsrkForr", + "type": "arrow" + }, + { + "id": "UW2vwnBaiRHvonL7pl21d", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 71, + "versionNonce": 1742923309, + "isDeleted": false, + "id": "vsG0L_Oy2c9JHJXMoITGK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2184.1260428739974, + "y": 1834.2592697010527, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 146.03334045410156, + "height": 45, + "seed": 251469104, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "incentive", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "incentive", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 245, + "versionNonce": 684171043, + "isDeleted": false, + "id": "MLLQQJX_gOT9E6PWE0j0b", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2125.9594127536034, + "y": 1893.60728419426, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 351.9333190917969, + "height": 50, + "seed": 1006141232, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- collects rewards for each block\n- distributes rewards to validators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- collects rewards for each block\n- distributes rewards to validators", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "arrow", + "version": 167, + "versionNonce": 1616877709, + "isDeleted": false, + "id": "oxbKMbcxmmyRC0Uszo0za", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2129.3809792312736, + "y": 2123.1169376311764, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 134.11423659478078, + "seed": 122708272, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "pqenW44iuTnPJ2rbsdpy-" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MpZJWSpMDFY-n7qtelImN", + "focus": 0.007299270072990775, + "gap": 13.686265910680504 + }, + "endBinding": { + "elementId": "XLV0apwvUf-95Ikn5tQNT", + "focus": 0.7397769516728665, + "gap": 6.843132955340479 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + -134.11423659478078 + ] + ] + }, + { + "type": "text", + "version": 85, + "versionNonce": 158188227, + "isDeleted": false, + "id": "pqenW44iuTnPJ2rbsdpy-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2034.0643135818596, + "y": 2000.6022930175748, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 190.63333129882812, + "height": 50, + "seed": 1395974960, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Token transfer of \nrewards", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "oxbKMbcxmmyRC0Uszo0za", + "originalText": "Token transfer of rewards", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 179, + "versionNonce": 1599536877, + "isDeleted": false, + "id": "ofMiK1KHYfIynAOsVqat2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1034.5346034578095, + "y": 1126.7733539294657, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 420.8526767534381, + "height": 155.19617473249173, + "seed": 587171120, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "aPy186FNWzxJXzWqaVRUa", + "type": "arrow" + }, + { + "id": "tzgh7GO8Oh0n7_ZVppRoK", + "type": "arrow" + }, + { + "id": "8hxi0sEZD-C2JdYmCn6cp", + "type": "arrow" + }, + { + "id": "FcQpoNogq2Nm9AsrkForr", + "type": "arrow" + }, + { + "id": "z9FxhoUfh_p_HuDlfW_jQ", + "type": "arrow" + }, + { + "id": "13tbfOPspPTrQ4jvUBqS8", + "type": "arrow" + }, + { + "id": "1mR6xqwOtINXSyrvyV3k2", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 173, + "versionNonce": 617509475, + "isDeleted": false, + "id": "Gilx9LAuX4U_RNPl-sB2W", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1163.6343090996388, + "y": 1179.9326947637362, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 159.3000030517578, + "height": 45, + "seed": 1564107568, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "restaking", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "restaking", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 479, + "versionNonce": 726833485, + "isDeleted": false, + "id": "aPy186FNWzxJXzWqaVRUa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1461.4916988730813, + "y": 1039.0216170971448, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 572.1403160644345, + "height": 78.14832800509294, + "seed": 551999440, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "P_2FRlE44FXHr9d948KkW" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-fU10jT3GNlbPP5PigmFn", + "focus": 0.398490965135845, + "gap": 1.0269660263367086 + }, + "endBinding": { + "elementId": "IE6raU1vN7ZaQgjKbsP_I", + "focus": -0.7305730201150965, + "gap": 6.788235874332145 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 294.7520049795039, + -56.04071882745848 + ], + [ + 572.1403160644345, + 22.10760917763446 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 1538797059, + "isDeleted": false, + "id": "P_2FRlE44FXHr9d948KkW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1666.4420003426449, + "y": 1129.4827501592385, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 166.39999389648438, + "height": 25, + "seed": 190769456, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "BTC Timestamps", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aPy186FNWzxJXzWqaVRUa", + "originalText": "BTC Timestamps", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 403, + "versionNonce": 2108417965, + "isDeleted": false, + "id": "k5XmRkToqNRcQ9CVHvwF2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1982.2536206914542, + "y": 967.9428668123143, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 532.4898851115133, + "height": 163.54145853838384, + "seed": 165561808, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "6BWf0-an_-SUp_llydlU8" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XRsX4CQXeYyrF6iFbcwkf", + "focus": 0.9422646641437531, + "gap": 8.553916194175144 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -221.4459472165404, + 80.81038345601917 + ], + [ + -532.4898851115133, + 163.54145853838384 + ] + ] + }, + { + "type": "text", + "version": 73, + "versionNonce": 1898542499, + "isDeleted": false, + "id": "6BWf0-an_-SUp_llydlU8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1650.8817233032134, + "y": 1061.0514206058338, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 137.53334045410156, + "height": 25, + "seed": 1941780784, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Register chain", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "k5XmRkToqNRcQ9CVHvwF2", + "originalText": "Register chain", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 419, + "versionNonce": 680290829, + "isDeleted": false, + "id": "tzgh7GO8Oh0n7_ZVppRoK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1467.3627628830934, + "y": 1193.531092469634, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 562.7927884957437, + "height": 95.25602118528127, + "seed": 123328464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "XQrFDC1Wtt82SkVbiUZhk" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "focus": -0.29308730123075766, + "gap": 11.975482671845839 + }, + "endBinding": { + "elementId": "oXlZjJ6fHpM0JEGCEuxaN", + "focus": -0.19340385431013918, + "gap": 15.397049149516192 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 303.3802275387802, + 22.4666597391913 + ], + [ + 562.7927884957437, + 95.25602118528127 + ] + ] + }, + { + "type": "text", + "version": 95, + "versionNonce": 1146145091, + "isDeleted": false, + "id": "XQrFDC1Wtt82SkVbiUZhk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1637.3704330345856, + "y": 1244.2715682210107, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 224.43333435058594, + "height": 25, + "seed": 1601658160, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Validator Set Update ", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tzgh7GO8Oh0n7_ZVppRoK", + "originalText": "Validator Set Update ", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 141, + "versionNonce": 2095947885, + "isDeleted": false, + "id": "oWuiUx2ktjtNyWW0RbdKk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1046.4551890486468, + "y": 1440.5144253359658, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 400.3232778874167, + "height": 246.35278639225638, + "seed": 2043765552, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "8hxi0sEZD-C2JdYmCn6cp", + "type": "arrow" + }, + { + "id": "K1_BQ-eNxQoevuNfEo-FQ", + "type": "arrow" + }, + { + "id": "BNsbfOJxpu3SiumhnV4Pp", + "type": "arrow" + }, + { + "id": "9-neyMzbXyIAzVdCZgE37", + "type": "arrow" + }, + { + "id": "hIGhfc7N5UnvINvkJx3X-", + "type": "arrow" + }, + { + "id": "13tbfOPspPTrQ4jvUBqS8", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 120, + "versionNonce": 1250732259, + "isDeleted": false, + "id": "KkM2jbot4Uh37CwxneSPN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1147.3914001399187, + "y": 1444.2006912466468, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 180.8333282470703, + "height": 45, + "seed": 1759719728, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "8hxi0sEZD-C2JdYmCn6cp", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "btcstaking", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "btcstaking", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 282, + "versionNonce": 1889077965, + "isDeleted": false, + "id": "J2YNwz75b65WxH5u9WeIc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1099.4894694525353, + "y": 1522.3673213670406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 322.8666687011719, + "height": 150, + "seed": 1205748176, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "ve_qswif0oRQu7buEaV8r", + "type": "arrow" + }, + { + "id": "fHa7mU34mFfK6rFMd8b8W", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- Keeps track of validators\n and the chains they intend\n to validate\n- Keeps track of validator\n delegations and the delegation\n status", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Keeps track of validators\n and the chains they intend\n to validate\n- Keeps track of validator\n delegations and the delegation\n status", + "lineHeight": 1.25, + "baseline": 143 + }, + { + "type": "arrow", + "version": 420, + "versionNonce": 1231787139, + "isDeleted": false, + "id": "8hxi0sEZD-C2JdYmCn6cp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1318.2779315261082, + "y": 1431.9605091417902, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.719910124743592, + "height": 141.43706428565724, + "seed": 1251876656, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "nxZiS14IwWClAol9VPStB" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "KkM2jbot4Uh37CwxneSPN", + "focus": 0.8632990176278252, + "gap": 12.240182104856558 + }, + "endBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "gap": 8.5539161941756, + "focus": -0.34215644960803865 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.719910124743592, + -83.49186430683312 + ], + [ + 2.0091487127742766, + -141.43706428565724 + ] + ] + }, + { + "type": "text", + "version": 156, + "versionNonce": 69740845, + "isDeleted": false, + "id": "nxZiS14IwWClAol9VPStB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1209.5724047754566, + "y": 1312.0120393864413, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 217.76666259765625, + "height": 50, + "seed": 1396410320, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Validator delegations \nupdates", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8hxi0sEZD-C2JdYmCn6cp", + "originalText": "Validator delegations updates", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "arrow", + "version": 592, + "versionNonce": 1353452579, + "isDeleted": false, + "id": "hIGhfc7N5UnvINvkJx3X-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1079.5571254671095, + "y": 2436.190270338002, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 513.8320265311296, + "height": 777.7919193599225, + "seed": 379098576, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "DF9hwRGA6A-StP9JFI7aX" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": -0.20660851527352694, + "gap": 3.4215664776695576 + }, + "endBinding": { + "elementId": "oWuiUx2ktjtNyWW0RbdKk", + "focus": 0.10310326856195558, + "gap": 6.843132955340707 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -513.8320265311296, + -504.681055456359 + ], + [ + -39.945069373803335, + -777.7919193599225 + ] + ] + }, + { + "type": "text", + "version": 95, + "versionNonce": 1761901453, + "isDeleted": false, + "id": "DF9hwRGA6A-StP9JFI7aX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 593.9475744120535, + "y": 1933.8817467030049, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 172.8000030517578, + "height": 50, + "seed": 464463312, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Update validated\nchains", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hIGhfc7N5UnvINvkJx3X-", + "originalText": "Update validated\nchains", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "text", + "version": 227, + "versionNonce": 1950126019, + "isDeleted": false, + "id": "zXtTvKHeYbl7wvN-HfmHE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1166.2100157671043, + "y": 2046.3963913166065, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 120.80000305175781, + "height": 45, + "seed": 1582267696, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "finality", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "finality", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 294, + "versionNonce": 1734836717, + "isDeleted": false, + "id": "bDN6LTGscJ4-rx6W10tOa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1087.5139867806893, + "y": 2116.273804675835, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 332.3999938964844, + "height": 100, + "seed": 395664176, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- collects consensus blocks\n- processes signatures for those\n blocks\n- manages validators' randomness", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- collects consensus blocks\n- processes signatures for those\n blocks\n- manages validators' randomness", + "lineHeight": 1.25, + "baseline": 93 + }, + { + "type": "arrow", + "version": 858, + "versionNonce": 1178312547, + "isDeleted": false, + "id": "ve_qswif0oRQu7buEaV8r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1222.8871583970135, + "y": 1700.5534776389031, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 429.53360373344105, + "height": 393.56723401888803, + "seed": 1663580112, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "3OiTc64HIVc5Xc_a4K3uX" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "J2YNwz75b65WxH5u9WeIc", + "focus": -0.7714634996564614, + "gap": 28.1861562718625 + }, + "endBinding": { + "elementId": "3Kg-UpYyQzQOkeMXdE-kQ", + "focus": -0.6192360789399399, + "gap": 18.826009837054926 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -429.53360373344105, + 119.75482671845793 + ], + [ + -195.25797918542162, + 393.56723401888803 + ] + ] + }, + { + "type": "text", + "version": 107, + "versionNonce": 73055309, + "isDeleted": false, + "id": "3OiTc64HIVc5Xc_a4K3uX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 742.1535160827945, + "y": 1879.661200388436, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 218.73333740234375, + "height": 25, + "seed": 58452272, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Active Delegations. ", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ve_qswif0oRQu7buEaV8r", + "originalText": "Active Delegations. ", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 634, + "versionNonce": 284670723, + "isDeleted": false, + "id": "hqjhvGv_tzZo3qWQSfyYg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1154.5266748083152, + "y": 2425.925570904992, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.7224116137363126, + "height": 167.65675740584174, + "seed": 921937200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "eXyYOog56hrQydgVhh81L" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": -0.30935251798561925, + "gap": 13.686265910679367 + }, + "endBinding": { + "elementId": "3Kg-UpYyQzQOkeMXdE-kQ", + "focus": 0.4482758620689669, + "gap": 15.397049149516874 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.7224116137363126, + -167.65675740584174 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 1740090029, + "isDeleted": false, + "id": "eXyYOog56hrQydgVhh81L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1061.8119850352657, + "y": 2221.817848155459, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.26666259765625, + "height": 25, + "seed": 1865183696, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Commit Randomness", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hqjhvGv_tzZo3qWQSfyYg", + "originalText": "Commit Randomness", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 623, + "versionNonce": 1357865635, + "isDeleted": false, + "id": "3EYM65srfkkFAkRf4Nf0R", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1300.3578469906981, + "y": 2429.3471373826624, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 2.152347117853651, + "height": 167.65675740584174, + "seed": 228175824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "U-q3fKx5KPNHy_pOInWwj" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": 0.2990766760337138, + "gap": 10.264699433009127 + }, + "endBinding": { + "elementId": "3Kg-UpYyQzQOkeMXdE-kQ", + "focus": -0.29608275394268135, + "gap": 18.818615627187114 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 2.152347117853651, + -167.65675740584174 + ] + ] + }, + { + "type": "text", + "version": 63, + "versionNonce": 36162829, + "isDeleted": false, + "id": "U-q3fKx5KPNHy_pOInWwj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1280.4006156977334, + "y": 2225.2394146331294, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 43.63333511352539, + "height": 25, + "seed": 1266543408, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Vote", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "3EYM65srfkkFAkRf4Nf0R", + "originalText": "Vote", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 376, + "versionNonce": 1859196483, + "isDeleted": false, + "id": "h-FxVGkngS5rHxj52BuW6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1407.3048698649611, + "y": 2432.7687038603326, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 636.5369474245567, + "height": 773.2740239534719, + "seed": 1532766512, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "qEq5dpdfjX320ooAKeKu4" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": 0.4066956388810014, + "gap": 6.843132955338888 + }, + "endBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "focus": 0.7656817003628804, + "gap": 17.1078323883512 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 362.8116292109387, + -509.81340517286435 + ], + [ + 636.5369474245567, + -773.2740239534719 + ] + ] + }, + { + "type": "text", + "version": 63, + "versionNonce": 1931780973, + "isDeleted": false, + "id": "qEq5dpdfjX320ooAKeKu4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1708.9518170259294, + "y": 2002.8375935845645, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 43.63333511352539, + "height": 25, + "seed": 36369872, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Vote", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "h-FxVGkngS5rHxj52BuW6", + "originalText": "Vote", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 330, + "versionNonce": 964384227, + "isDeleted": false, + "id": "I7ZDOOFXqDv-baF94yeo3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1474.150998757425, + "y": 2442.237573703209, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 579.786270501495, + "height": 700.859076510359, + "seed": 1652303312, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "glu1IuiXYQo3aBygB2zmT" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "X9v7_TtCdmuXMD42T1sms", + "focus": 0.6093173465436234, + "gap": 8.553916194173667 + }, + "endBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "focus": 0.557975591517287, + "gap": 8.553916194175372 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 579.786270501495, + -700.859076510359 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 787983821, + "isDeleted": false, + "id": "glu1IuiXYQo3aBygB2zmT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1669.9954264411488, + "y": 2049.0287410331125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.26666259765625, + "height": 25, + "seed": 619977168, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Commit Randomness", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "I7ZDOOFXqDv-baF94yeo3", + "originalText": "Commit Randomness", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 549, + "versionNonce": 536650115, + "isDeleted": false, + "id": "FcQpoNogq2Nm9AsrkForr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2062.6604329167044, + "y": 1873.2272975900787, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 600.4300197501159, + "height": 629.7139202663616, + "seed": 1239850960, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "eSK86We9_NvTofYu3o94Q" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XLV0apwvUf-95Ikn5tQNT", + "focus": -0.7855089749192722, + "gap": 6.8431329553411615 + }, + "endBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "focus": -0.32714528964879064, + "gap": 6.843132955340934 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -299.89011425279364, + -499.05811272919595 + ], + [ + -600.4300197501159, + -629.7139202663616 + ] + ] + }, + { + "type": "text", + "version": 165, + "versionNonce": 1689938989, + "isDeleted": false, + "id": "eSK86We9_NvTofYu3o94Q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1647.6703201897897, + "y": 1336.6691848608828, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 230.1999969482422, + "height": 75, + "seed": 1924245296, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Rewards for validators\n+\nIBC Token Transfer", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FcQpoNogq2Nm9AsrkForr", + "originalText": "Rewards for validators\n+\nIBC Token Transfer", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "rectangle", + "version": 164, + "versionNonce": 731085091, + "isDeleted": false, + "id": "b6KcYnozKEq-fYIxBcM6W", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1055.0091052428222, + "y": 1787.803422819494, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 381.50466226023036, + "height": 135.15187586797447, + "seed": 1487472080, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "CLmpSDPqzoyNVdM8bskrx", + "type": "arrow" + }, + { + "id": "z9FxhoUfh_p_HuDlfW_jQ", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 132, + "versionNonce": 609640077, + "isDeleted": false, + "id": "zrxYY_x_rHMydclL1MoYB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1164.4992325282697, + "y": 1788.0681222525045, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 146.03334045410156, + "height": 45, + "seed": 1799196112, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "incentive", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "incentive", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 189, + "versionNonce": 967728323, + "isDeleted": false, + "id": "6fpmEHIkMrtnzYIcUTeCL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1085.8032035418541, + "y": 1852.548486462218, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 320.29998779296875, + "height": 50, + "seed": 1832829392, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- responsible of keeping track of\n rewards and distributing them", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- responsible of keeping track of\n rewards and distributing them", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "arrow", + "version": 83, + "versionNonce": 1149568237, + "isDeleted": false, + "id": "CLmpSDPqzoyNVdM8bskrx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1232.9305620816738, + "y": 2025.6022930175754, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 97.5146446136016, + "seed": 921810896, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "E6JaMRl5d55kxyucKUuJW" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "3Kg-UpYyQzQOkeMXdE-kQ", + "focus": -0.06034482758620939, + "gap": 15.3970491495146 + }, + "endBinding": { + "elementId": "b6KcYnozKEq-fYIxBcM6W", + "focus": 0.06726457399103282, + "gap": 5.1323497165053595 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + -97.5146446136016 + ] + ] + }, + { + "type": "text", + "version": 87, + "versionNonce": 1978387555, + "isDeleted": false, + "id": "E6JaMRl5d55kxyucKUuJW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1131.880559029916, + "y": 1964.3449707107748, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 202.10000610351562, + "height": 25, + "seed": 248690480, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Votes for BBN block", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "CLmpSDPqzoyNVdM8bskrx", + "originalText": "Votes for BBN block", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 629, + "versionNonce": 147840845, + "isDeleted": false, + "id": "z9FxhoUfh_p_HuDlfW_jQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1358.8593134638436, + "y": 1290.523444856133, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 184.6486846829648, + "height": 544.9712940542827, + "seed": 836724528, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "YoXfOV_j-a0jUPeXhx2MO" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "focus": -0.30836404398432243, + "gap": 8.5539161941756 + }, + "endBinding": { + "elementId": "b6KcYnozKEq-fYIxBcM6W", + "focus": 0.7400351664342889, + "gap": 5.095400090642215 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 184.6486846829648, + 414.73217957878023 + ], + [ + 82.7498541298512, + 544.9712940542827 + ] + ] + }, + { + "type": "text", + "version": 239, + "versionNonce": 337266691, + "isDeleted": false, + "id": "YoXfOV_j-a0jUPeXhx2MO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1444.8746668479803, + "y": 1667.7556244349132, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 197.26666259765625, + "height": 75, + "seed": 309293360, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Tranfer IBC token \nrewards for \nvalidators ", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "z9FxhoUfh_p_HuDlfW_jQ", + "originalText": "Tranfer IBC token rewards for validators ", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "arrow", + "version": 364, + "versionNonce": 126905773, + "isDeleted": false, + "id": "13tbfOPspPTrQ4jvUBqS8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1099.9374015473259, + "y": 1288.8567431531974, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.7554594203365923, + "height": 140.75032341526526, + "seed": 511240144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "lkhN2mqZyaYAd_7BxfLGI" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "gap": 6.887214491240002, + "focus": 0.6810510191094379 + }, + "endBinding": { + "elementId": "oWuiUx2ktjtNyWW0RbdKk", + "gap": 10.907358767503183, + "focus": -0.7442179053198268 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -1.7554594203365923, + 140.75032341526526 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 1245204387, + "isDeleted": false, + "id": "lkhN2mqZyaYAd_7BxfLGI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1017.0424308526667, + "y": 1329.3451002036502, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 166.13333129882812, + "height": 25, + "seed": 1827579696, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Slashing Evidence", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "13tbfOPspPTrQ4jvUBqS8", + "originalText": "Slashing Evidence", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 138, + "versionNonce": 1341141005, + "isDeleted": false, + "id": "1mR6xqwOtINXSyrvyV3k2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2052.2436372524044, + "y": 1613.410430087221, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 601.086675291074, + "height": 326.20957309184996, + "seed": 26137904, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "yJOfaLWucAHV8j0_f7duZ" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "focus": -0.19395149162936365, + "gap": 8.706012425464678 + }, + "endBinding": { + "elementId": "ofMiK1KHYfIynAOsVqat2", + "focus": -0.3327234872122553, + "gap": 5.231328333413558 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -298.0595084087972, + -84.45019404915911 + ], + [ + -601.086675291074, + -326.20957309184996 + ] + ] + }, + { + "type": "text", + "version": 118, + "versionNonce": 175399747, + "isDeleted": false, + "id": "yJOfaLWucAHV8j0_f7duZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1671.117463194193, + "y": 1516.4602360380618, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 166.13333129882812, + "height": 25, + "seed": 1320932304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Slashing Evidence", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1mR6xqwOtINXSyrvyV3k2", + "originalText": "Slashing Evidence", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "text", + "version": 459, + "versionNonce": 1340647021, + "isDeleted": false, + "id": "AMHVs2xnShhWLbgiqPQ1i", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 133.9477782536867, + "y": 1058.7706948312298, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 743.9666748046875, + "height": 315, + "seed": 1844066608, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770351, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegators:\n- Select a validator to delegate to\nbased on their trustworthiness and chains\nthey validate.\n- Create a staking transaction to this validator\nand send it to Babylon.\n- No changes to the btc-staker program apart\nfrom displaying some extra information to stakers\nrelated to the chains that validators are validating.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Delegators:\n- Select a validator to delegate to\nbased on their trustworthiness and chains\nthey validate.\n- Create a staking transaction to this validator\nand send it to Babylon.\n- No changes to the btc-staker program apart\nfrom displaying some extra information to stakers\nrelated to the chains that validators are validating.", + "lineHeight": 1.25, + "baseline": 305 + }, + { + "type": "rectangle", + "version": 115, + "versionNonce": 2100101859, + "isDeleted": false, + "id": "WuOw12xQRyeRBIfbt2RUi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -251.6434945541322, + "y": 1412.2437130774852, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 337.95940969626497, + "height": 229.08657972699848, + "seed": 608342832, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "EowCRh9q6NgH7YHsP82Ls", + "type": "arrow" + } + ], + "updated": 1704783770351, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 63, + "versionNonce": 2051231949, + "isDeleted": false, + "id": "lzkJTxBLC5Q1dbND9uCr4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -140.50248062717253, + "y": 1477.093783681356, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.30000305175781, + "height": 45, + "seed": 1349112112, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 83, + "versionNonce": 988851843, + "isDeleted": false, + "id": "EowCRh9q6NgH7YHsP82Ls", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 283.6479194614283, + "y": 1527.921094919831, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.25926848852328, + "height": 2.2681839576930543, + "seed": 329796048, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "Av0BFVBhVSkowWlgxcSqg" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "l-VxcloF1H5GLPDnWvCWJ", + "focus": 0.2227832994456643, + "gap": 15.194994216274296 + }, + "endBinding": { + "elementId": "WuOw12xQRyeRBIfbt2RUi", + "focus": 0.0475855602437881, + "gap": 9.072735830772217 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -188.25926848852328, + 2.2681839576930543 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 748269357, + "isDeleted": false, + "id": "Av0BFVBhVSkowWlgxcSqg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 132.68495315539906, + "y": 1516.5551868986774, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.66666412353516, + "height": 25, + "seed": 41875760, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Staking txs", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EowCRh9q6NgH7YHsP82Ls", + "originalText": "Staking txs", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "text", + "version": 607, + "versionNonce": 1182656035, + "isDeleted": false, + "id": "ko-eqD_IxIdEoWROr-Qrd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1059.3668329924526, + "y": 2616.5494416817437, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 686.2666625976562, + "height": 350, + "seed": 1055652816, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Validators:\n- Register to Babylon as BTC Validators\n- Signal intent to validate chains\n (don't necessarily need to validate Babylon)\n- Commit public randomness and vote for blocks\n in each of the chains they validate.\n- Changes needed to btc-validator program\n - Modular validator implementation\n - Implementation of interface for communicating\n with the BBN smart contract", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Validators:\n- Register to Babylon as BTC Validators\n- Signal intent to validate chains\n (don't necessarily need to validate Babylon)\n- Commit public randomness and vote for blocks\n in each of the chains they validate.\n- Changes needed to btc-validator program\n - Modular validator implementation\n - Implementation of interface for communicating\n with the BBN smart contract", + "lineHeight": 1.25, + "baseline": 340 + }, + { + "type": "text", + "version": 2966, + "versionNonce": 1431650701, + "isDeleted": false, + "id": "FPcO8ulMV9FksOzCpt5rg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2721.945673981461, + "y": 889.6341386388548, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 990.7333374023438, + "height": 1575, + "seed": 591997744, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "A consumer chain deploys the BBN smart contract.\nThis is an extension of the smart contract developed\nfor receiving BTC timestamps.\n\nThe contract communicates with Babylon via the\nzoneconcierge module.\n\nThe btcstaking component maintains the active BTC\nvalidators and their delegations. It accomplishes this\nthrough the `ValidatorSetUpdate` message, which is\nsent from Babylon to the chain every time there is\nan update on the BTC validator set that has signalled\nintent to validate this chain. It contains:\n- Validators that are affected (either new or old)\n- For each validator affected, the validator's delegations\n that have changed state. A state change might be\n a delegation expiring, unbonding, or a new delegation.\n- This message also lets a chain know if a validator\n has been slashed.\n- Through the connection to the timestamping components,\n the module can verify proofs of inclusion of delegations.\n\nThe finality component acts in exactly the same way\nas the finality module of Babylon. It receives public\nrandomness and votes from validators. If a validator\ndouble votes, then the finality component constructs\na `SlashingEvidence` message which contains a proof\nthat the validator misbehaved. It knows whether a block\nhas been finalized by calculating the voting power table\nthrough a connection to the btcstaking component.\n\nThe incentive component is responsible for receiving tokens\nfrom the underlying consumer chain and sending them to\nBabylon to be distributed to validators + delegators\nthrough IBC token transfer. This module has several unknowns\n1. How can we transfer rewards from the underlying chain to\n the smart contract in the minimal invasive way?\n2. Do we need to provide any proofs that the validators for\n which we say that they should get rewards have actually voted?\n3. Should the message contain precise validator/delegator distributions\n or can we only specify the validators and Babylon does the further\n partitioning based on commissions? The size of the message will be\n significantly affected by our solution. \n\n", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "A consumer chain deploys the BBN smart contract.\nThis is an extension of the smart contract developed\nfor receiving BTC timestamps.\n\nThe contract communicates with Babylon via the\nzoneconcierge module.\n\nThe btcstaking component maintains the active BTC\nvalidators and their delegations. It accomplishes this\nthrough the `ValidatorSetUpdate` message, which is\nsent from Babylon to the chain every time there is\nan update on the BTC validator set that has signalled\nintent to validate this chain. It contains:\n- Validators that are affected (either new or old)\n- For each validator affected, the validator's delegations\n that have changed state. A state change might be\n a delegation expiring, unbonding, or a new delegation.\n- This message also lets a chain know if a validator\n has been slashed.\n- Through the connection to the timestamping components,\n the module can verify proofs of inclusion of delegations.\n\nThe finality component acts in exactly the same way\nas the finality module of Babylon. It receives public\nrandomness and votes from validators. If a validator\ndouble votes, then the finality component constructs\na `SlashingEvidence` message which contains a proof\nthat the validator misbehaved. It knows whether a block\nhas been finalized by calculating the voting power table\nthrough a connection to the btcstaking component.\n\nThe incentive component is responsible for receiving tokens\nfrom the underlying consumer chain and sending them to\nBabylon to be distributed to validators + delegators\nthrough IBC token transfer. This module has several unknowns\n1. How can we transfer rewards from the underlying chain to\n the smart contract in the minimal invasive way?\n2. Do we need to provide any proofs that the validators for\n which we say that they should get rewards have actually voted?\n3. Should the message contain precise validator/delegator distributions\n or can we only specify the validators and Babylon does the further\n partitioning based on commissions? The size of the message will be\n significantly affected by our solution. \n\n", + "lineHeight": 1.25, + "baseline": 1565 + }, + { + "type": "arrow", + "version": 106, + "versionNonce": 1187195331, + "isDeleted": false, + "id": "fHa7mU34mFfK6rFMd8b8W", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1113.8032479770864, + "y": 2031.4579335276883, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 149.70014120774158, + "height": 347.0321455270371, + "seed": 748618192, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "s5gU5UWgV4kGKDi61s7wd" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "3Kg-UpYyQzQOkeMXdE-kQ", + "focus": -0.16985167163782902, + "gap": 9.541408639401766 + }, + "endBinding": { + "elementId": "J2YNwz75b65WxH5u9WeIc", + "focus": 0.24079731712523425, + "gap": 12.058466633610578 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -131.55466954619715, + -172.38198078467212 + ], + [ + 18.145471661544434, + -347.0321455270371 + ] + ] + }, + { + "type": "text", + "version": 75, + "versionNonce": 1504675821, + "isDeleted": false, + "id": "s5gU5UWgV4kGKDi61s7wd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 925.5985769050103, + "y": 1824.0759527430162, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.30000305175781, + "height": 70, + "seed": 2054009808, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Slashing\nEvidence", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fHa7mU34mFfK6rFMd8b8W", + "originalText": "Slashing\nEvidence", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 127, + "versionNonce": 14612835, + "isDeleted": false, + "id": "UW2vwnBaiRHvonL7pl21d", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2281.917986189009, + "y": 1743.3985709006706, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 88.45917435002889, + "seed": 218065200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "yGbujroL94mA1fR0swcei" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bapEN-Oda9yJXFVTnxLpI", + "focus": 0.06063935461046908, + "gap": 6.91864524623054 + }, + "endBinding": { + "elementId": "XLV0apwvUf-95Ikn5tQNT", + "focus": -0.07686179374676609, + "gap": 6.596030768205992 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 88.45917435002889 + ] + ] + }, + { + "type": "text", + "version": 65, + "versionNonce": 1001521741, + "isDeleted": false, + "id": "yGbujroL94mA1fR0swcei", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2254.667986189009, + "y": 1775.128158075685, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 54.5, + "height": 25, + "seed": 74131920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Votes", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UW2vwnBaiRHvonL7pl21d", + "originalText": "Votes", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "text", + "version": 812, + "versionNonce": 304076035, + "isDeleted": false, + "id": "kt56wdKguh6DZf9tP4qAn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 902.8621399116323, + "y": 186.96074383862174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 834.8333129882812, + "height": 595, + "seed": 377777104, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "In the Babylon node we have to perform\nthe following updates:\n\nbtcstaking:\n- Track the chains a validator has opted to delegate to\n- Handle slashing evidence from zoneconcierge module\n- At the EndBlocker, send updates happened to validators\n and the chains they validate to the zoneconcierge module,\n as well as necessary proofs.\n\nincentive:\n- Handle IBC token transfer rewards and distribute them\n\nzoneconcierge:\n- Define message interfaces for the messages in between\n the consumer chain and Babylon.\n", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "In the Babylon node we have to perform\nthe following updates:\n\nbtcstaking:\n- Track the chains a validator has opted to delegate to\n- Handle slashing evidence from zoneconcierge module\n- At the EndBlocker, send updates happened to validators\n and the chains they validate to the zoneconcierge module,\n as well as necessary proofs.\n\nincentive:\n- Handle IBC token transfer rewards and distribute them\n\nzoneconcierge:\n- Define message interfaces for the messages in between\n the consumer chain and Babylon.\n", + "lineHeight": 1.25, + "baseline": 585 + }, + { + "type": "rectangle", + "version": 514, + "versionNonce": 3551405, + "isDeleted": false, + "id": "5f7uu4kjIEUFzrjUpX9s9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7007.774178879419, + "y": 945.8884991612201, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 709.0507674670008, + "height": 676.0071462436719, + "seed": 260055696, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "gFiD_drGpBD-W-UZK0NwH", + "type": "arrow" + }, + { + "id": "qQPd6opx3debj8-1YUcb5", + "type": "arrow" + }, + { + "id": "Giojol2wXs96hmIzH19d5", + "type": "arrow" + }, + { + "id": "JA8TyLpYrRi8MLw2LeRyM", + "type": "arrow" + }, + { + "id": "Py60shrNSM5iaxY-OKjWA", + "type": "arrow" + }, + { + "id": "BC0vFMLOBUKZMe0II0jva", + "type": "arrow" + }, + { + "id": "TV7k2DcOhFbsd7McAHI4P", + "type": "arrow" + }, + { + "id": "J9v9y3Ffg_2wkgPNTj4bZ", + "type": "arrow" + }, + { + "id": "UIkSG6S2UtMt2LBbzOoZ-", + "type": "arrow" + }, + { + "id": "yiNt60KG3azTY3duMRMfA", + "type": "arrow" + }, + { + "id": "p8LtEmaFkk4DaHFUquc3E", + "type": "arrow" + }, + { + "id": "IAu1_oifIihVnusXFf1rq", + "type": "arrow" + }, + { + "id": "41lDBkHDzYp0h0NaioZ7H", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 347, + "versionNonce": 1778662563, + "isDeleted": false, + "id": "h7nbiMGIF04_TjXN9QLka", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7245.357156711066, + "y": 1183.1682813344537, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 197.39999389648438, + "height": 67.76958097087783, + "seed": 1316835472, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 54.21566477670226, + "fontFamily": 1, + "text": "Babylon", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon", + "lineHeight": 1.25, + "baseline": 48 + }, + { + "type": "line", + "version": 1664, + "versionNonce": 1424331533, + "isDeleted": false, + "id": "rSPnMAmMv2vPjlPHFXx6y", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7269.398037932977, + "y": 1984.669247107432, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1369751184, + "groupIds": [ + "Q6ZejYyHbpZtFHQfOP-l1", + "PpL-5dWT6RL3rLrqil-Ta" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1343, + "versionNonce": 70955075, + "isDeleted": false, + "id": "LLJ7_mQxgISTTRF2bad6s", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7276.075643711146, + "y": 1948.9103628295952, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 430861456, + "groupIds": [ + "Q6ZejYyHbpZtFHQfOP-l1", + "PpL-5dWT6RL3rLrqil-Ta" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1641, + "versionNonce": 1983490413, + "isDeleted": false, + "id": "JYU01rQ4H1EnPGQoT1LLQ", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7341.250933964052, + "y": 1981.2476806297627, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1347574416, + "groupIds": [ + "IOGzXgpb4fYBRMN1eprja", + "9TXhTcAvCdtvf49g7cdRk" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1320, + "versionNonce": 1654763491, + "isDeleted": false, + "id": "O014FmQmJ71K_GoUzpWHW", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7347.92853974222, + "y": 1945.4887963519259, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 675842192, + "groupIds": [ + "IOGzXgpb4fYBRMN1eprja", + "9TXhTcAvCdtvf49g7cdRk" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1646, + "versionNonce": 1434186701, + "isDeleted": false, + "id": "LZ3Rfy3tszlQSel9s_t9U", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7409.682263517456, + "y": 1982.9584638685965, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1028191888, + "groupIds": [ + "FcHkQqYEqoetsNL_xBe7r", + "rlxBycV5nP5gXrhTMBlpS" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1325, + "versionNonce": 1195883395, + "isDeleted": false, + "id": "fDQA_hdnLDrWFMjyHSEK8", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 7416.359869295625, + "y": 1947.1995795907596, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1946920080, + "groupIds": [ + "FcHkQqYEqoetsNL_xBe7r", + "rlxBycV5nP5gXrhTMBlpS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 605, + "versionNonce": 2126901805, + "isDeleted": false, + "id": "Z2GPJMdSMH7oDinaJuQrP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7127.560925729765, + "y": 1859.0956272484063, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 475.5977403961617, + "height": 155.681274733995, + "seed": 1514705552, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Giojol2wXs96hmIzH19d5", + "type": "arrow" + }, + { + "id": "JA8TyLpYrRi8MLw2LeRyM", + "type": "arrow" + }, + { + "id": "Py60shrNSM5iaxY-OKjWA", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 273, + "versionNonce": 778785571, + "isDeleted": false, + "id": "xxHevteGCHHMo1nEdj1Po", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7154.121006844141, + "y": 1867.9142428755922, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 425.6000061035156, + "height": 45, + "seed": 112705680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Babylon BTC Validators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon BTC Validators", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 487, + "versionNonce": 1057039501, + "isDeleted": false, + "id": "Ofx5yKSduYv665xdEAj8Y", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6247.192327909113, + "y": 1086.0990059111277, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 234.37730372041068, + "height": 278.85766793012385, + "seed": 2070071952, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "gFiD_drGpBD-W-UZK0NwH", + "type": "arrow" + }, + { + "id": "qQPd6opx3debj8-1YUcb5", + "type": "arrow" + }, + { + "id": "9Wp2uIUthDigdWp8INFhj", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 246, + "versionNonce": 142276291, + "isDeleted": false, + "id": "FHjwRM3_Zj23khl_WU5hU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6267.721726775135, + "y": 1106.8931042101592, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 194.23333740234375, + "height": 45, + "seed": 2009790608, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Delegators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Delegators", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "line", + "version": 1570, + "versionNonce": 166136557, + "isDeleted": false, + "id": "yuYjkBvz_V4k9nRYVQC3d", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6344.5490759026125, + "y": 1230.4912413973393, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1392338576, + "groupIds": [ + "8gtCuOni2QgOJPFtlJCjX", + "hBU-bZ9kdrBuNBG9mLj7t" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1250, + "versionNonce": 873801315, + "isDeleted": false, + "id": "WFuv3lHbFfjQU6Q9Tj2SX", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6351.226681680781, + "y": 1194.7323571195025, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 967971984, + "groupIds": [ + "8gtCuOni2QgOJPFtlJCjX", + "hBU-bZ9kdrBuNBG9mLj7t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1520, + "versionNonce": 536945997, + "isDeleted": false, + "id": "HVgMQwm8lMt2nedrobX5K", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6341.127509424942, + "y": 1292.079437995404, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 27329168, + "groupIds": [ + "YPaDmuQObNsJZibRZAkyi", + "TO0_QepGN2HHhmF2gQr6W" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1199, + "versionNonce": 940073475, + "isDeleted": false, + "id": "_dSBYYCtV9AagDJo_srC0", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6347.805115203111, + "y": 1256.3205537175668, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 159527056, + "groupIds": [ + "YPaDmuQObNsJZibRZAkyi", + "TO0_QepGN2HHhmF2gQr6W" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1522, + "versionNonce": 1957229485, + "isDeleted": false, + "id": "zlevQdrH8nwV-yCPKT3Eo", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6342.838292663778, + "y": 1348.5352848769626, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 420322960, + "groupIds": [ + "pFgvakPz64Rvvprivmbns", + "6KL8W5Otw2Lqa8wvd7RcF" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1202, + "versionNonce": 1028860323, + "isDeleted": false, + "id": "ytrPTr01YU5fMLw0pm4iw", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 6349.515898441946, + "y": 1312.7764005991253, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1090210960, + "groupIds": [ + "pFgvakPz64Rvvprivmbns", + "6KL8W5Otw2Lqa8wvd7RcF" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 2165, + "versionNonce": 1409807885, + "isDeleted": false, + "id": "gFiD_drGpBD-W-UZK0NwH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6484.991198107195, + "y": 1190.5853278676152, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 511.3744663230573, + "height": 59.97751480944885, + "seed": 990067344, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "wKERMThinXI5vuKcpVLXn" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Ofx5yKSduYv665xdEAj8Y", + "focus": -0.07803258904145931, + "gap": 3.421566477670268 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "gap": 11.408514449167342, + "focus": -0.010134800773305403 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.7743528699259, + -46.31969183609317 + ], + [ + 511.3744663230573, + 13.657822973355678 + ] + ] + }, + { + "type": "text", + "version": 107, + "versionNonce": 1227095363, + "isDeleted": false, + "id": "wKERMThinXI5vuKcpVLXn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6619.4988807500695, + "y": 1131.765636031522, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 230.53334045410156, + "height": 25, + "seed": 1239246992, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Delegate to Validators", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gFiD_drGpBD-W-UZK0NwH", + "originalText": "Delegate to Validators", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 2077, + "versionNonce": 1226288237, + "isDeleted": false, + "id": "qQPd6opx3debj8-1YUcb5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6994.654881191416, + "y": 1242.6758727340282, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 504.53133336771646, + "height": 47.04080725299514, + "seed": 423745168, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "xdxtSuzMCbQQP3Yrg2sWJ" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "gap": 13.119297688002916, + "focus": 0.19037803160455372 + }, + "endBinding": { + "elementId": "Ofx5yKSduYv665xdEAj8Y", + "focus": -0.210158554590088, + "gap": 8.553916194175628 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -254.75698049779055, + 19.63380677711666 + ], + [ + -504.53133336771646, + -27.407000475878476 + ] + ] + }, + { + "type": "text", + "version": 130, + "versionNonce": 920214755, + "isDeleted": false, + "id": "xdxtSuzMCbQQP3Yrg2sWJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6673.981236570091, + "y": 1224.8096795111449, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 131.8333282470703, + "height": 75, + "seed": 501777552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Validators\nand chains\nthey validate", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qQPd6opx3debj8-1YUcb5", + "originalText": "Validators\nand chains\nthey validate", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "rectangle", + "version": 478, + "versionNonce": 1134663373, + "isDeleted": false, + "id": "r4v0FbTF5HqPDSOjESQ79", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6991.626703538119, + "y": 148.7898298301879, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1825.0488030341673, + "height": 229.08657972699848, + "seed": 857896080, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9Wp2uIUthDigdWp8INFhj", + "type": "arrow" + }, + { + "id": "2tbxDclyApG1yvPHvk2aZ", + "type": "arrow" + }, + { + "id": "NwIiDKmNYbPb8ZDJEKuaO", + "type": "arrow" + }, + { + "id": "d7CFBP2RyYRBwJ9VuOR81", + "type": "arrow" + }, + { + "id": "HR75PFP27DrbOlqk-Yx1H", + "type": "arrow" + }, + { + "id": "PuvSeOD6rWEj-1r-IYOSX", + "type": "arrow" + }, + { + "id": "_s7MHtQZYSEdXqgtUKkZg", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 200, + "versionNonce": 1912719491, + "isDeleted": false, + "id": "etZDKrUaw8P-mfQio3nl2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7877.355035664942, + "y": 240.24786174626792, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.30000305175781, + "height": 45, + "seed": 772104848, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 1393, + "versionNonce": 2118071597, + "isDeleted": false, + "id": "9Wp2uIUthDigdWp8INFhj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6357.811938993152, + "y": 1077.2516868183918, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 609.315590833362, + "height": 783.5586668509086, + "seed": 1784320144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "nhnXz7aHpuv2QL1Bp9b3k" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Ofx5yKSduYv665xdEAj8Y", + "focus": -0.5096817711054381, + "gap": 8.84731909273603 + }, + "endBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": 0.9031989905637419, + "gap": 24.49917371160518 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 228.65442261700719, + -332.10804930115273 + ], + [ + 609.315590833362, + -783.5586668509086 + ] + ] + }, + { + "type": "text", + "version": 89, + "versionNonce": 1885061155, + "isDeleted": false, + "id": "nhnXz7aHpuv2QL1Bp9b3k", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6084.084366623871, + "y": 1187.3363897244976, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 107.56666564941406, + "height": 25, + "seed": 2144992912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Staking Tx", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "9Wp2uIUthDigdWp8INFhj", + "originalText": "Staking Tx", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 89, + "versionNonce": 609879949, + "isDeleted": false, + "id": "Giojol2wXs96hmIzH19d5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7349.227919226818, + "y": 1839.0264914636102, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 141.9091269984483, + "height": 206.95081020607017, + "seed": 226287679, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "upmIldamSUC3mOwQOMLeC" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Z2GPJMdSMH7oDinaJuQrP", + "focus": 0.35975658828674184, + "gap": 20.069135784796117 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": -0.5531108222723243, + "gap": 10.180035852647848 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -141.9091269984483, + -97.56252481143315 + ], + [ + 0, + -206.95081020607017 + ] + ] + }, + { + "type": "text", + "version": 54, + "versionNonce": 1809171395, + "isDeleted": false, + "id": "upmIldamSUC3mOwQOMLeC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7292.8112512885855, + "y": 1718.051086360575, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 112.83333587646484, + "height": 35, + "seed": 376267697, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Register", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Giojol2wXs96hmIzH19d5", + "originalText": "Register", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 71, + "versionNonce": 942378477, + "isDeleted": false, + "id": "JA8TyLpYrRi8MLw2LeRyM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7352.184359372619, + "y": 1839.0264914636102, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 198.08148976866732, + "seed": 2145670015, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "NnsXI67fP2pBCEULJQEHA" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Z2GPJMdSMH7oDinaJuQrP", + "focus": -0.05540579963332548, + "gap": 20.069135784796117 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.0285316755990152, + "gap": 19.049356290050696 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + -198.08148976866732 + ] + ] + }, + { + "type": "text", + "version": 56, + "versionNonce": 1552831331, + "isDeleted": false, + "id": "NnsXI67fP2pBCEULJQEHA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7312.817694486144, + "y": 1722.4857465792766, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.73332977294922, + "height": 35, + "seed": 1793711281, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "EOTS", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JA8TyLpYrRi8MLw2LeRyM", + "originalText": "EOTS", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 129, + "versionNonce": 951928909, + "isDeleted": false, + "id": "Py60shrNSM5iaxY-OKjWA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7361.053679810022, + "y": 1839.0264914636102, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 144.86556714424933, + "height": 209.9072503518712, + "seed": 1231758065, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "MnF3U1mH0nAv9_UwrXYKH" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Z2GPJMdSMH7oDinaJuQrP", + "focus": -0.4595977125459293, + "gap": 20.069135784796117 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.5394991804414715, + "gap": 7.223595706846822 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 144.86556714424933, + -85.73676422822905 + ], + [ + 0, + -209.9072503518712 + ] + ] + }, + { + "type": "text", + "version": 50, + "versionNonce": 852750083, + "isDeleted": false, + "id": "MnF3U1mH0nAv9_UwrXYKH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7475.369247717211, + "y": 1735.7897272353812, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 61.099998474121094, + "height": 35, + "seed": 603054673, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Vote", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Py60shrNSM5iaxY-OKjWA", + "originalText": "Vote", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 572, + "versionNonce": 132691629, + "isDeleted": false, + "id": "7AAf0l0osVIkrHWsFIYH3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8088.58538943969, + "y": 940.9401521194957, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 709.0507674670008, + "height": 676.0071462436719, + "seed": 300997585, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "BC0vFMLOBUKZMe0II0jva", + "type": "arrow" + }, + { + "id": "AiSrsmg9fiSgRXLkdnfge", + "type": "arrow" + }, + { + "id": "dLxA2jEbQoL0B_g3dEmVM", + "type": "arrow" + }, + { + "id": "TV7k2DcOhFbsd7McAHI4P", + "type": "arrow" + }, + { + "id": "J9v9y3Ffg_2wkgPNTj4bZ", + "type": "arrow" + }, + { + "id": "UIkSG6S2UtMt2LBbzOoZ-", + "type": "arrow" + }, + { + "id": "yiNt60KG3azTY3duMRMfA", + "type": "arrow" + }, + { + "id": "kA6i6mSq8vH4WFRsFxf24", + "type": "arrow" + }, + { + "id": "FdZGZ0PzL8Xkr4hEgdBVK", + "type": "arrow" + }, + { + "id": "6TzS7ktzcWBuV0JdftQZ6", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 477, + "versionNonce": 1361385123, + "isDeleted": false, + "id": "nwyH63OrI1Fz9XZ4fZbU4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8237.475162897306, + "y": 980.1384445240622, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 398.3666687011719, + "height": 67.76958097087783, + "seed": 1474771377, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 54.21566477670226, + "fontFamily": 1, + "text": "Consumer Chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Consumer Chain", + "lineHeight": 1.25, + "baseline": 48 + }, + { + "type": "line", + "version": 1725, + "versionNonce": 274159885, + "isDeleted": false, + "id": "cdVtPe6bhwbE4xeB3JxhT", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8350.209248493247, + "y": 1979.720900065708, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 790027153, + "groupIds": [ + "olJS_vJcDIcz6B9rC7Zja", + "XOrc6_SIRthg-5yHqE9rX" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1404, + "versionNonce": 1177569859, + "isDeleted": false, + "id": "udXIZ2TbZfA-27huUiDCG", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8356.886854271415, + "y": 1943.9620157878712, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 666321265, + "groupIds": [ + "olJS_vJcDIcz6B9rC7Zja", + "XOrc6_SIRthg-5yHqE9rX" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1702, + "versionNonce": 729984877, + "isDeleted": false, + "id": "Zf0Dnk4DhRIMaJcxv8xMh", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8422.062144524323, + "y": 1976.2993335880387, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 1594878801, + "groupIds": [ + "UHwt3EfekmoX7sR5x-Vab", + "XO3DPqNmrMDh35ID9Y8U_" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1381, + "versionNonce": 1188301283, + "isDeleted": false, + "id": "xXiewZJ4Oj8fGFs-Du3Ft", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8428.739750302491, + "y": 1940.540449310202, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1568168241, + "groupIds": [ + "UHwt3EfekmoX7sR5x-Vab", + "XO3DPqNmrMDh35ID9Y8U_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1707, + "versionNonce": 1202993613, + "isDeleted": false, + "id": "zcMc61rmVYIFwrQAqWgYC", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8490.493474077726, + "y": 1978.0101168268725, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 26.41763133716407, + "height": 23.524142033662113, + "seed": 878185233, + "groupIds": [ + "_dj15OYIXruJL2SEfg8Ct", + "FCIxn0rNF-LloFcJmEutw" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.958774709762374, + -15.5882868897761 + ], + [ + 12.680463041838754, + -23.524142033662113 + ], + [ + 22.40215137391513, + -17.28882727775165 + ], + [ + 26.41763133716407, + -1.6186460582798874 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1386, + "versionNonce": 1746851203, + "isDeleted": false, + "id": "OuRr0ekwr6xcLmQrTqKD2", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 8497.171079855896, + "y": 1942.2512325490356, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 13.525827244628017, + "height": 11.835098839049556, + "seed": 1546027249, + "groupIds": [ + "_dj15OYIXruJL2SEfg8Ct", + "FCIxn0rNF-LloFcJmEutw" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 674, + "versionNonce": 1308981293, + "isDeleted": false, + "id": "cb13-7-UUFl8kPeinKC98", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8208.372136290036, + "y": 1854.1472802066824, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 525.8572228747772, + "height": 155.681274733995, + "seed": 1382759121, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "BC0vFMLOBUKZMe0II0jva", + "type": "arrow" + }, + { + "id": "AiSrsmg9fiSgRXLkdnfge", + "type": "arrow" + }, + { + "id": "dLxA2jEbQoL0B_g3dEmVM", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 344, + "versionNonce": 424619299, + "isDeleted": false, + "id": "9ZX4NT61ejU3WaC0qd2SS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8234.932217404412, + "y": 1862.9658958338682, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 451.8999938964844, + "height": 45, + "seed": 2008922289, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Consumer BTC Validators", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Consumer BTC Validators", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 499, + "versionNonce": 1214245517, + "isDeleted": false, + "id": "BC0vFMLOBUKZMe0II0jva", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8461.862716463207, + "y": 1834.078144421886, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 738.4127815225593, + "height": 206.95081020607017, + "seed": 767916689, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "G9WuYHob5xRbJC3APFZYF" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "cb13-7-UUFl8kPeinKC98", + "gap": 20.069135784796345, + "focus": 0.8142024780010457 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.2515879784907131, + "gap": 6.624988594227943 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -516.6797705874833, + -79.82388393662723 + ], + [ + -738.4127815225593, + -206.95081020607017 + ] + ] + }, + { + "type": "text", + "version": 116, + "versionNonce": 1574146243, + "isDeleted": false, + "id": "G9WuYHob5xRbJC3APFZYF", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8231.713334850407, + "y": 1719.015619610453, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 112.83333587646484, + "height": 35, + "seed": 892993649, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Register", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BC0vFMLOBUKZMe0II0jva", + "originalText": "Register", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 370, + "versionNonce": 1199378669, + "isDeleted": false, + "id": "AiSrsmg9fiSgRXLkdnfge", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8453.029023881314, + "y": 1834.078144421886, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 7.2746557395512355, + "height": 198.08148976866732, + "seed": 1273154129, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "W2VLqI3SMCRUpK182Gf_p" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "cb13-7-UUFl8kPeinKC98", + "gap": 20.069135784796345, + "focus": -0.05540579963333314 + }, + "endBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "gap": 19.049356290050923, + "focus": 0.028531675599015205 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -7.2746557395512355, + -198.08148976866732 + ] + ] + }, + { + "type": "text", + "version": 117, + "versionNonce": 1196064867, + "isDeleted": false, + "id": "W2VLqI3SMCRUpK182Gf_p", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8393.628905046415, + "y": 1717.5373995375526, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.73332977294922, + "height": 35, + "seed": 177690673, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "EOTS", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AiSrsmg9fiSgRXLkdnfge", + "originalText": "EOTS", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 443, + "versionNonce": 149318477, + "isDeleted": false, + "id": "dLxA2jEbQoL0B_g3dEmVM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8449.738770680353, + "y": 1834.078144421886, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 141.9091269984483, + "height": 212.86369049767222, + "seed": 54471185, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "O_dY1q5qXRXf8iWEHc-PU" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "cb13-7-UUFl8kPeinKC98", + "focus": -0.45959771254593923, + "gap": 20.069135784796345 + }, + "endBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "focus": 0.5197357457499742, + "gap": 4.267155561046025 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 136.99168683418884, + -85.73676422822905 + ], + [ + -4.91744016425946, + -212.86369049767222 + ] + ] + }, + { + "type": "text", + "version": 112, + "versionNonce": 797080579, + "isDeleted": false, + "id": "O_dY1q5qXRXf8iWEHc-PU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8556.180458277482, + "y": 1730.8413801936572, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 61.099998474121094, + "height": 35, + "seed": 517062641, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Vote", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dLxA2jEbQoL0B_g3dEmVM", + "originalText": "Vote", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 66, + "versionNonce": 1559591341, + "isDeleted": false, + "id": "TV7k2DcOhFbsd7McAHI4P", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8079.468635239666, + "y": 1040.7876520973393, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 337.03417662131415, + "height": 0, + "seed": 1067290783, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "bqvlj9zX8ymsU2YaGX_ga" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "focus": 0.7045963181523743, + "gap": 9.116754200023934 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": -0.7192362433934629, + "gap": 25.609512271931635 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -337.03417662131415, + 0 + ] + ] + }, + { + "type": "text", + "version": 54, + "versionNonce": 846857123, + "isDeleted": false, + "id": "bqvlj9zX8ymsU2YaGX_ga", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7854.534878990777, + "y": 1023.2876520973393, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 112.83333587646484, + "height": 35, + "seed": 840853553, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Register", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "TV7k2DcOhFbsd7McAHI4P", + "originalText": "Register", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 112, + "versionNonce": 1288944653, + "isDeleted": false, + "id": "J9v9y3Ffg_2wkgPNTj4bZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7727.652257889346, + "y": 1170.8710185125835, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 407.98874012053875, + "height": 0, + "seed": 348964529, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "w2mNuWfGcUnCCs8jhNv1x" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": -0.33437828105365447, + "gap": 10.827311542926509 + }, + "endBinding": { + "elementId": "8h8jUw2Xae2g79QIr6Ynx", + "focus": 0.6144578313253011, + "gap": 26.607961312209227 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 407.98874012053875, + 0 + ] + ] + }, + { + "type": "text", + "version": 88, + "versionNonce": 1751095107, + "isDeleted": false, + "id": "w2mNuWfGcUnCCs8jhNv1x", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7814.015560842192, + "y": 1153.3710185125835, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 176.13333129882812, + "height": 35, + "seed": 262956607, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Timestamping", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "J9v9y3Ffg_2wkgPNTj4bZ", + "originalText": "Timestamping", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 111, + "versionNonce": 8041069, + "isDeleted": false, + "id": "UIkSG6S2UtMt2LBbzOoZ-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7724.695817743545, + "y": 1292.0850644904247, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 416.8580605579418, + "height": 5.91288029160205, + "seed": 727475839, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "MD5nN7_r_vjq-nEM-CaEg" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.03886912265705265, + "gap": 7.870871397125484 + }, + "endBinding": { + "elementId": "8h8jUw2Xae2g79QIr6Ynx", + "focus": 0.15963349024882342, + "gap": 20.695081020607176 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 416.8580605579418, + -5.91288029160205 + ] + ] + }, + { + "type": "text", + "version": 63, + "versionNonce": 1603385059, + "isDeleted": false, + "id": "MD5nN7_r_vjq-nEM-CaEg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7778.372002513872, + "y": 1274.5850644904247, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 253.3333282470703, + "height": 35, + "seed": 18241425, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Validator Updates", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UIkSG6S2UtMt2LBbzOoZ-", + "originalText": "Validator Updates", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 89, + "versionNonce": 1689509069, + "isDeleted": false, + "id": "yiNt60KG3azTY3duMRMfA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7733.565138180948, + "y": 1404.429790030863, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 405.0322999747377, + "height": 0, + "seed": 2020080369, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "ECoxw8OQHmONjgMggZSdS" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.3566166967837289, + "gap": 16.74019183452856 + }, + "endBinding": { + "elementId": "8h8jUw2Xae2g79QIr6Ynx", + "focus": -0.3373493975903621, + "gap": 23.6515211664082 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 405.0322999747377, + 0 + ] + ] + }, + { + "type": "text", + "version": 57, + "versionNonce": 1453253251, + "isDeleted": false, + "id": "ECoxw8OQHmONjgMggZSdS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7828.878438082036, + "y": 1386.929790030863, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 158.23333740234375, + "height": 35, + "seed": 453654623, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegations", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "yiNt60KG3azTY3duMRMfA", + "originalText": "Delegations", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 601, + "versionNonce": 286833453, + "isDeleted": false, + "id": "op4Vo_dXXuuyI3LWXuHY7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7014.687566213829, + "y": 544.8635834255102, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 692.7322271923852, + "height": 229.08657972699848, + "seed": 398641169, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "2tbxDclyApG1yvPHvk2aZ", + "type": "arrow" + }, + { + "id": "NwIiDKmNYbPb8ZDJEKuaO", + "type": "arrow" + }, + { + "id": "p8LtEmaFkk4DaHFUquc3E", + "type": "arrow" + }, + { + "id": "IAu1_oifIihVnusXFf1rq", + "type": "arrow" + }, + { + "id": "41lDBkHDzYp0h0NaioZ7H", + "type": "arrow" + }, + { + "id": "HR75PFP27DrbOlqk-Yx1H", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 508, + "versionNonce": 910531107, + "isDeleted": false, + "id": "KJXyNQSnKtsj6cwHFNgLk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7282.519907868243, + "y": 642.2344956331922, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 127.93333435058594, + "height": 45, + "seed": 1563532785, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Monitor", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Monitor", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 140, + "versionNonce": 1846726029, + "isDeleted": false, + "id": "8h8jUw2Xae2g79QIr6Ynx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8162.248959322094, + "y": 1076.2649338469514, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 570.5929481395942, + "height": 490.76906420296655, + "seed": 1298913361, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "J9v9y3Ffg_2wkgPNTj4bZ", + "type": "arrow" + }, + { + "id": "UIkSG6S2UtMt2LBbzOoZ-", + "type": "arrow" + }, + { + "id": "yiNt60KG3azTY3duMRMfA", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 125, + "versionNonce": 1265917379, + "isDeleted": false, + "id": "Nxnq5HES_Hq-nOd_N-Np7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8230.247082675516, + "y": 1281.1721841988226, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 430.20001220703125, + "height": 45, + "seed": 1844676977, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Babylon Smart Contract", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon Smart Contract", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 197, + "versionNonce": 450597869, + "isDeleted": false, + "id": "2tbxDclyApG1yvPHvk2aZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7047.671024355115, + "y": 393.3272601669196, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 5.91288029160205, + "height": 135.99624670684625, + "seed": 1026158673, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "Z7iJryo9X1kyGxnperih-" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": 0.9396487515705894, + "gap": 15.450850609733209 + }, + "endBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": -0.859021466502033, + "gap": 15.540076551744392 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 5.91288029160205, + 135.99624670684625 + ] + ] + }, + { + "type": "text", + "version": 107, + "versionNonce": 1095347555, + "isDeleted": false, + "id": "Z7iJryo9X1kyGxnperih-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6935.127464500916, + "y": 443.8253835203427, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 231, + "height": 35, + "seed": 905388031, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BBN Delegations", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "2tbxDclyApG1yvPHvk2aZ", + "originalText": "BBN Delegations", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 176, + "versionNonce": 326929997, + "isDeleted": false, + "id": "NwIiDKmNYbPb8ZDJEKuaO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7310.794197331405, + "y": 399.24014045852164, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 115.30116568623907, + "seed": 1257324351, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "LGGjoxdozaSpmTTGkBIm5" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": 0.6502367572169409, + "gap": 21.36373090133526 + }, + "endBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": -0.14510507958411872, + "gap": 30.322277280749518 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 115.30116568623907 + ] + ] + }, + { + "type": "text", + "version": 143, + "versionNonce": 1528039683, + "isDeleted": false, + "id": "LGGjoxdozaSpmTTGkBIm5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7195.894195805526, + "y": 421.8907233016412, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 229.8000030517578, + "height": 70, + "seed": 8036031, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BBN Delegation \nSlashings", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NwIiDKmNYbPb8ZDJEKuaO", + "originalText": "BBN Delegation Slashings", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 107, + "versionNonce": 382680237, + "isDeleted": false, + "id": "p8LtEmaFkk4DaHFUquc3E", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7059.496784938319, + "y": 780.6209192668512, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 156.6913277274532, + "seed": 426436785, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "gupdfS_pUDrd25gse7Vr3" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": 0.870630477504707, + "gap": 6.670756114342453 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": -0.8541074675268376, + "gap": 8.576252166915879 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 156.6913277274532 + ] + ] + }, + { + "type": "text", + "version": 110, + "versionNonce": 1850863779, + "isDeleted": false, + "id": "gupdfS_pUDrd25gse7Vr3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 6911.930119288905, + "y": 823.9665831305778, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 295.1333312988281, + "height": 70, + "seed": 820496287, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegation Censorship\nAlarm + Submission", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "p8LtEmaFkk4DaHFUquc3E", + "originalText": "Delegation Censorship Alarm + Submission", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 78, + "versionNonce": 1283268365, + "isDeleted": false, + "id": "IAu1_oifIihVnusXFf1rq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7369.923000247425, + "y": 795.4031199958561, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 141.9091269984483, + "seed": 1732152223, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "66mim4O2W71gOBFm2TRgB" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": -0.025606778750138594, + "gap": 21.45295684334735 + }, + "endBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.021503220881459183, + "gap": 8.576252166915879 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 141.9091269984483 + ] + ] + }, + { + "type": "text", + "version": 77, + "versionNonce": 481420355, + "isDeleted": false, + "id": "66mim4O2W71gOBFm2TRgB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7239.739665896839, + "y": 848.8576834950802, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 260.3666687011719, + "height": 35, + "seed": 1751089041, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegation Slashed", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IAu1_oifIihVnusXFf1rq", + "originalText": "Delegation Slashed", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 651, + "versionNonce": 721401197, + "isDeleted": false, + "id": "YmCLaNHb2U4lTQau2fVIa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8084.918898993796, + "y": 541.9071432797097, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 716.3837483587934, + "height": 229.08657972699848, + "seed": 412685503, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "d7CFBP2RyYRBwJ9VuOR81", + "type": "arrow" + }, + { + "id": "kA6i6mSq8vH4WFRsFxf24", + "type": "arrow" + }, + { + "id": "PuvSeOD6rWEj-1r-IYOSX", + "type": "arrow" + }, + { + "id": "FdZGZ0PzL8Xkr4hEgdBVK", + "type": "arrow" + }, + { + "id": "6TzS7ktzcWBuV0JdftQZ6", + "type": "arrow" + }, + { + "id": "_s7MHtQZYSEdXqgtUKkZg", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 137, + "versionNonce": 1309828067, + "isDeleted": false, + "id": "5CJsonXtdPq7fglvCZSzD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8378.069089965567, + "y": 642.5811127058057, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 127.93333435058594, + "height": 45, + "seed": 1182783185, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Monitor", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Monitor", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 87, + "versionNonce": 497057741, + "isDeleted": false, + "id": "d7CFBP2RyYRBwJ9VuOR81", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8142.153113829583, + "y": 396.2837003127205, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.4654610321886139, + "height": 124.17048612364215, + "seed": 905994143, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "wecg1dr72sJv2S98tJLJq" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "gap": 18.407290755534063, + "focus": -0.2601604656835446 + }, + "endBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "gap": 21.45295684334701, + "focus": -0.8364881058378715 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.4654610321886139, + 124.17048612364215 + ] + ] + }, + { + "type": "text", + "version": 69, + "versionNonce": 1786900355, + "isDeleted": false, + "id": "wecg1dr72sJv2S98tJLJq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8035.270545476778, + "y": 440.8689433745416, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.56666564941406, + "height": 35, + "seed": 500132401, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "CZ Delegations", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d7CFBP2RyYRBwJ9VuOR81", + "originalText": "CZ Delegations", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 88, + "versionNonce": 1786314285, + "isDeleted": false, + "id": "41lDBkHDzYp0h0NaioZ7H", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7647.82837395272, + "y": 934.3558068485036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 138.95268685264728, + "seed": 73728447, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "KACAVfr7omqA9QMVFA92_" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "5f7uu4kjIEUFzrjUpX9s9", + "focus": 0.8053832657422193, + "gap": 11.532692312716676 + }, + "endBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": -0.8279525129211429, + "gap": 21.45295684334758 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + -138.95268685264728 + ] + ] + }, + { + "type": "text", + "version": 70, + "versionNonce": 1413416739, + "isDeleted": false, + "id": "KACAVfr7omqA9QMVFA92_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7554.011708303306, + "y": 847.3794634221799, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 187.63333129882812, + "height": 35, + "seed": 1468914641, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Double Signing", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "41lDBkHDzYp0h0NaioZ7H", + "originalText": "Double Signing", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 77, + "versionNonce": 1483911309, + "isDeleted": false, + "id": "HR75PFP27DrbOlqk-Yx1H", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7570.960930161893, + "y": 529.3235068737656, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 133.03980656104505, + "seed": 104998385, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "6EBYbdqdsvxFlygFaIrYS" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "op4Vo_dXXuuyI3LWXuHY7", + "focus": 0.6060270970866081, + "gap": 15.540076551744619 + }, + "endBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": 0.3651301536039766, + "gap": 18.407290755534177 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + -133.03980656104505 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 565717699, + "isDeleted": false, + "id": "6EBYbdqdsvxFlygFaIrYS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 7428.927589707791, + "y": 445.3036035932431, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 284.0666809082031, + "height": 35, + "seed": 361256415, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Slashing Transaction", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HR75PFP27DrbOlqk-Yx1H", + "originalText": "Slashing Transaction", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 79, + "versionNonce": 356980461, + "isDeleted": false, + "id": "kA6i6mSq8vH4WFRsFxf24", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8166.566947436617, + "y": 792.4466798500551, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.7812910939501307, + "height": 127.12692626944306, + "seed": 527872607, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "kqKNzuVAYSeDj0SbJn-k_" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "gap": 21.452956843346897, + "focus": 0.7682033625041633 + }, + "endBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "gap": 21.366545999997584, + "focus": -0.7838800448607602 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.7812910939501307, + 127.12692626944306 + ] + ] + }, + { + "type": "text", + "version": 87, + "versionNonce": 395041379, + "isDeleted": false, + "id": "kqKNzuVAYSeDj0SbJn-k_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8017.638733818481, + "y": 821.0101429847766, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 295.1333312988281, + "height": 70, + "seed": 999466257, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegation Censorship\nAlarm + Submission", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kA6i6mSq8vH4WFRsFxf24", + "originalText": "Delegation Censorship Alarm + Submission", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 122, + "versionNonce": 1535095117, + "isDeleted": false, + "id": "PuvSeOD6rWEj-1r-IYOSX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8447.019308617064, + "y": 396.2837003127206, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 2.7477778498869156, + "height": 133.03980656104522, + "seed": 678169745, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "zv8udvWwe2ibUCsZwNt_K" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": -0.5903684338962878, + "gap": 18.407290755534234 + }, + "endBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "focus": 0.025743367901733382, + "gap": 12.583636405943821 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 2.7477778498869156, + 133.03980656104522 + ] + ] + }, + { + "type": "text", + "version": 96, + "versionNonce": 1653118467, + "isDeleted": false, + "id": "zv8udvWwe2ibUCsZwNt_K", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8344.427438822604, + "y": 427.8036035932432, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 197.36666870117188, + "height": 70, + "seed": 1890635135, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "CZ Delegation\nSlashings", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PuvSeOD6rWEj-1r-IYOSX", + "originalText": "CZ Delegation\nSlashings", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 119, + "versionNonce": 1484128173, + "isDeleted": false, + "id": "FdZGZ0PzL8Xkr4hEgdBVK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8460.702959526669, + "y": 777.6644791210501, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 2.851548081915098, + "height": 153.73488758165217, + "seed": 41207537, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "v7NamQiGRSsEzbIvAK6i3" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "focus": -0.05506415488509982, + "gap": 6.670756114341998 + }, + "endBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "focus": 0.022988742610528204, + "gap": 9.540785416793483 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -2.851548081915098, + 153.73488758165217 + ] + ] + }, + { + "type": "text", + "version": 89, + "versionNonce": 296541603, + "isDeleted": false, + "id": "v7NamQiGRSsEzbIvAK6i3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8380.463423383542, + "y": 819.5319229118762, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 143.03334045410156, + "height": 70, + "seed": 315946879, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Delegation\nSlashed", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FdZGZ0PzL8Xkr4hEgdBVK", + "originalText": "Delegation\nSlashed", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 110, + "versionNonce": 1381204493, + "isDeleted": false, + "id": "6TzS7ktzcWBuV0JdftQZ6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8706.157512064185, + "y": 925.4864864111003, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.391290840734655, + "height": 138.95268685264716, + "seed": 420177631, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "RTHhfCqiV8xCLXbpUHzHE" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7AAf0l0osVIkrHWsFIYH3", + "focus": 0.7013174102681039, + "gap": 15.453665708395533 + }, + "endBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "focus": -0.7468760262840102, + "gap": 15.54007655174496 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.391290840734655, + -138.95268685264716 + ] + ] + }, + { + "type": "text", + "version": 75, + "versionNonce": 1344745795, + "isDeleted": false, + "id": "RTHhfCqiV8xCLXbpUHzHE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8649.34151991686, + "y": 821.0101429847766, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 90.13333129882812, + "height": 70, + "seed": 1915108593, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Double\nSigning", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6TzS7ktzcWBuV0JdftQZ6", + "originalText": "Double\nSigning", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 113, + "versionNonce": 236042349, + "isDeleted": false, + "id": "_s7MHtQZYSEdXqgtUKkZg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8702.051447998578, + "y": 532.2799470195666, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 5.267820324528657, + "height": 135.99624670684602, + "seed": 1904669841, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "DDpUDxUaQ4qyIK36KonaP" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "YmCLaNHb2U4lTQau2fVIa", + "focus": 0.7273291224032434, + "gap": 9.627196260143023 + }, + "endBinding": { + "elementId": "r4v0FbTF5HqPDSOjESQ79", + "focus": -0.8587960334431806, + "gap": 18.407290755534234 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -5.267820324528657, + -135.99624670684602 + ] + ] + }, + { + "type": "text", + "version": 87, + "versionNonce": 1599475939, + "isDeleted": false, + "id": "DDpUDxUaQ4qyIK36KonaP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8606.128636573501, + "y": 429.2818236661436, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 164.73333740234375, + "height": 70, + "seed": 991005695, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Slashing\nTransaction", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_s7MHtQZYSEdXqgtUKkZg", + "originalText": "Slashing\nTransaction", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 338, + "versionNonce": 1970938573, + "isDeleted": false, + "id": "cm5FyddK0vz7subeXAVIp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 9028.48592204179, + "y": 642.5811127058059, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 668.566650390625, + "height": 540, + "seed": 2146321215, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Design Choices\n1. Monitor per CZ running:\n a. slasher routine\n b. monitor for liveness\n c. monitor for slashed delegations\n2. Both finality votes and EOTS are\n submitted to the PoS chain.\n\n\nDoes not include rewards.\n\n ", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Design Choices\n1. Monitor per CZ running:\n a. slasher routine\n b. monitor for liveness\n c. monitor for slashed delegations\n2. Both finality votes and EOTS are\n submitted to the PoS chain.\n\n\nDoes not include rewards.\n\n ", + "lineHeight": 1.25, + "baseline": 527 + }, + { + "type": "rectangle", + "version": 516, + "versionNonce": 1355283587, + "isDeleted": false, + "id": "Sm8uhB6wbwpfqUcz7JMxV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10604.268519753767, + "y": 1830.157171026204, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 366.5985780793235, + "height": 200.34111593401462, + "seed": 267247302, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 546, + "versionNonce": 2099659053, + "isDeleted": false, + "id": "90pwTHY-XyKJ9o-VPGi4H", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10715.140484298008, + "y": 1831.945562176414, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 140.53334045410156, + "height": 45, + "seed": 1571678534, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Epoching", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Epoching", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 430, + "versionNonce": 3896355, + "isDeleted": false, + "id": "s8RKBb4aWHmHLWMzJ8QCY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10995.996839072399, + "y": 1827.2007308804025, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 413.9016204121399, + "height": 200.34111593401505, + "seed": 2097260954, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 393, + "versionNonce": 1249874829, + "isDeleted": false, + "id": "fM9WsqwhZEesCWmOe_Z29", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11095.758832962998, + "y": 1831.8761888612244, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 221.73333740234375, + "height": 45, + "seed": 1555399898, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Checkpointing", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Checkpointing", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 550, + "versionNonce": 1302263747, + "isDeleted": false, + "id": "bnKi6MvGeOa6rXfF9wqLu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11430.593540505144, + "y": 1830.1571710262035, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 410.94518026633705, + "height": 195.9346530859773, + "seed": 2111804358, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "anB8X_sbi4sqniV1s2nox", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 567, + "versionNonce": 431987181, + "isDeleted": false, + "id": "U1sOemPDPOre1B9lDBySD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11491.592796450943, + "y": 1836.7373588952162, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.29998779296875, + "height": 45, + "seed": 1545921670, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC Checkpoint", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Checkpoint", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 564, + "versionNonce": 386991971, + "isDeleted": false, + "id": "MSM2z9sn1FcicEuDUqxc7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11865.190241937895, + "y": 1827.8975448608571, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 354.7728174961211, + "height": 195.93465308597774, + "seed": 940223002, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 724, + "versionNonce": 1676661837, + "isDeleted": false, + "id": "wUowYeaLX6Lc7qVoXSxkI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11894.04759369749, + "y": 1833.8373134907445, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 301.4666748046875, + "height": 45.383541253697636, + "seed": 1264550810, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36.30683300295811, + "fontFamily": 1, + "text": "BTC Light Client", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Light Client", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 564, + "versionNonce": 1927291651, + "isDeleted": false, + "id": "ozU2lapefevVSfaW8NkgQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12246.571020746223, + "y": 1827.1443361390739, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 473.0304233281604, + "height": 193.7314216619587, + "seed": 1829384006, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1051, + "versionNonce": 1156170413, + "isDeleted": false, + "id": "y4rme2MnApWx44nmHyZ73", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12342.11288161698, + "y": 1826.6037278087472, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 261.9333190917969, + "height": 45, + "seed": 1730406746, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "anB8X_sbi4sqniV1s2nox", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Zone Concierge", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Zone Concierge", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 423, + "versionNonce": 1616690659, + "isDeleted": false, + "id": "BnWROnQbk7tSdMVPxlq_t", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12778.680615824873, + "y": 1818.1323281609975, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 369.55501822512633, + "height": 198.08148976866732, + "seed": 729734682, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 246, + "versionNonce": 1866515917, + "isDeleted": false, + "id": "1SlzFWp6uSd7a3FudUT2_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12854.306548605986, + "y": 1826.497625763036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 222.8000030517578, + "height": 45, + "seed": 16703258, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC Staking", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Staking", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 443, + "versionNonce": 913377667, + "isDeleted": false, + "id": "zKbCKEPGeNiS66zSHK-7X", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13196.257990599379, + "y": 1817.8469834376199, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 369.55501822512633, + "height": 198.08148976866732, + "seed": 1719904602, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 275, + "versionNonce": 1814070317, + "isDeleted": false, + "id": "FpSCSFCEsIa3mIplF9fw6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13315.260684867038, + "y": 1823.1476418607629, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.96666717529297, + "height": 45, + "seed": 772233670, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Finality", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Finality", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 504, + "versionNonce": 1195041059, + "isDeleted": false, + "id": "lnzn99DGfieaS4acsCNvd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13620.121937783335, + "y": 1818.7969640376396, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 369.55501822512633, + "height": 198.08148976866732, + "seed": 41929158, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 212, + "versionNonce": 121206413, + "isDeleted": false, + "id": "l2EsbdDM-dCi_MiBbLtc3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13715.048095089276, + "y": 1824.8508311825635, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 157.76666259765625, + "height": 45, + "seed": 33119174, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Incentive", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Incentive", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 417, + "versionNonce": 1009304771, + "isDeleted": false, + "id": "gMWY7fP5rBveUymQJJJE_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10602.708019729667, + "y": 2412.167299041643, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 3434.3828279992267, + "height": 227.64589122667712, + "seed": 1232370778, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "YJq6laSN-yx57jGCvqtHl", + "type": "arrow" + }, + { + "id": "i8rUSngD92Yf-8G5kqyGx", + "type": "arrow" + }, + { + "id": "Th72X_O0Jcw9QslRrN6dG", + "type": "arrow" + }, + { + "id": "ubD9PQiYw8EIHbYNeDuZg", + "type": "arrow" + }, + { + "id": "u56fJvEUflV3M4Wf2lfxj", + "type": "arrow" + }, + { + "id": "XfoF5gyizY9lr0o_lqvKd", + "type": "arrow" + }, + { + "id": "4C_kRR9PuZj7jJ5ntwVfz", + "type": "arrow" + }, + { + "id": "ByxqHImFIZUnMJs-EiM9L", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 214, + "versionNonce": 189078765, + "isDeleted": false, + "id": "MEVGXPLS6yVNDWw7VK_Lb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12372.182313369209, + "y": 2492.7559572209757, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 181.73333740234375, + "height": 45, + "seed": 347616794, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "CometBFT", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "CometBFT", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 174, + "versionNonce": 215561315, + "isDeleted": false, + "id": "lxEY1ksynkZ0IDgBeWJc5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12348.568205776357, + "y": 1581.9021913139006, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 232.89999389648438, + "height": 45, + "seed": 772791238, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Babylon Node", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon Node", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 1426, + "versionNonce": 801961805, + "isDeleted": false, + "id": "YJq6laSN-yx57jGCvqtHl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10807.583263430915, + "y": 2398.590060457221, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.4923277655107086, + "height": 306.1434258317727, + "seed": 434382022, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "NHtt-cTZNhJN_NH4m_mjE" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": -0.8800456644337932, + "gap": 13.577238584422048 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": 0.8790265931258232, + "gap": 9.37746860153186 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -1.4923277655107086, + -306.1434258317727 + ] + ] + }, + { + "type": "text", + "version": 81, + "versionNonce": 958050307, + "isDeleted": false, + "id": "NHtt-cTZNhJN_NH4m_mjE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10664.325332689881, + "y": 2251.0360013445916, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 164.63333129882812, + "height": 105, + "seed": 155073754, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Staking and\nundelegating\nrequests", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "YJq6laSN-yx57jGCvqtHl", + "originalText": "Staking and\nundelegating\nrequests", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "arrow", + "version": 1390, + "versionNonce": 2133493165, + "isDeleted": false, + "id": "i8rUSngD92Yf-8G5kqyGx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11213.221570340298, + "y": 2397.6301588702913, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.6194059013741935, + "height": 297.2177106530394, + "seed": 1435581446, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "qNSCgzBcfz6bo5w7RWl8p" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": -0.6446441799007094, + "gap": 14.537140171351894 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": 0.6394634793652543, + "gap": 17.343282193335313 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.6194059013741935, + -297.2177106530394 + ] + ] + }, + { + "type": "text", + "version": 56, + "versionNonce": 1122744227, + "isDeleted": false, + "id": "qNSCgzBcfz6bo5w7RWl8p", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11073.563291535085, + "y": 2283.135955940119, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.36666870117188, + "height": 35, + "seed": 481772166, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BLS Signatures", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "i8rUSngD92Yf-8G5kqyGx", + "originalText": "BLS Signatures", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 1374, + "versionNonce": 2089240589, + "isDeleted": false, + "id": "Th72X_O0Jcw9QslRrN6dG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11637.041572624663, + "y": 2392.9222407232724, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.3639037471111806, + "height": 295.76768795080443, + "seed": 689566106, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "JsaRAOieRYHl47aT4c6QQ" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": -0.39789544538102267, + "gap": 19.245058318370866 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": 0.39322697199292445, + "gap": 14.085386748551286 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.3639037471111806, + -295.76768795080443 + ] + ] + }, + { + "type": "text", + "version": 57, + "versionNonce": 1752877891, + "isDeleted": false, + "id": "JsaRAOieRYHl47aT4c6QQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11489.010037405751, + "y": 2282.3545498476724, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 229.3000030517578, + "height": 35, + "seed": 1951528454, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BTC Checkpoints", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Th72X_O0Jcw9QslRrN6dG", + "originalText": "BTC Checkpoints", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 1378, + "versionNonce": 165461613, + "isDeleted": false, + "id": "ubD9PQiYw8EIHbYNeDuZg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12041.417675743578, + "y": 2390.775404040582, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.6802226912986953, + "height": 292.92403728766067, + "seed": 800448454, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "fAvRjCaIV6ZyczltkXT8Z" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": -0.16196502740821794, + "gap": 21.39189500106113 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": 0.16029890457536092, + "gap": 14.782200729004785 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.6802226912986953, + -292.92403728766067 + ] + ] + }, + { + "type": "text", + "version": 53, + "versionNonce": 505834211, + "isDeleted": false, + "id": "fAvRjCaIV6ZyczltkXT8Z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11931.91047245705, + "y": 2281.6295384965542, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.76666259765625, + "height": 35, + "seed": 622777882, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BTC Headers", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ubD9PQiYw8EIHbYNeDuZg", + "originalText": "BTC Headers", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 1092, + "versionNonce": 655414893, + "isDeleted": false, + "id": "u56fJvEUflV3M4Wf2lfxj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12489.26862768361, + "y": 2404.354343312355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.5464064750576654, + "height": 303.83867173444605, + "seed": 914038106, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "tRRUUx5HaZwZi_YntT6KA" + } + ], + "updated": 1704783776106, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": 0.09797406006495779, + "gap": 7.8129557292882055 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": -0.09566311441357997, + "gap": 17.446505553992324 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.3081885034589504, + -153.8657274751763 + ], + [ + -2.238217971598715, + -303.83867173444605 + ] + ] + }, + { + "type": "text", + "version": 52, + "versionNonce": 1216157315, + "isDeleted": false, + "id": "tRRUUx5HaZwZi_YntT6KA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12875.91637864943, + "y": 2279.273867832769, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 162.1999969482422, + "height": 35, + "seed": 1271585222, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "CZ Headers", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "u56fJvEUflV3M4Wf2lfxj", + "originalText": "CZ Headers", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 1426, + "versionNonce": 1516750637, + "isDeleted": false, + "id": "XfoF5gyizY9lr0o_lqvKd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12984.056454420994, + "y": 2398.6383491366378, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 6.927570128478692, + "height": 304.7497978708657, + "seed": 5515078, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "W6axddyxGkRZm0Lv23Kzs" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": 0.3878707941570865, + "gap": 13.528949905005447 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": -0.3803001705554596, + "gap": 10.819385241855457 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -6.927570128478692, + -304.7497978708657 + ] + ] + }, + { + "type": "text", + "version": 146, + "versionNonce": 909987363, + "isDeleted": false, + "id": "W6axddyxGkRZm0Lv23Kzs", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13249.808627964663, + "y": 2250.2827926228083, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 220.46665954589844, + "height": 105, + "seed": 1002467354, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "BTC Stake and\nFinality Provider\nregistration", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XfoF5gyizY9lr0o_lqvKd", + "originalText": "BTC Stake and\nFinality Provider\nregistration", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "arrow", + "version": 1393, + "versionNonce": 1369463181, + "isDeleted": false, + "id": "4C_kRR9PuZj7jJ5ntwVfz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13402.066426973137, + "y": 2393.55128435257, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.6082158574135974, + "height": 285.42871524912425, + "seed": 594403235, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "YBAXEJLMBkbdsj-9oEQKJ" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": 0.6306424420964465, + "gap": 18.61601468907338 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": -0.6272631651623789, + "gap": 25.053403079528948 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.6082158574135974, + -285.42871524912425 + ] + ] + }, + { + "type": "text", + "version": 92, + "versionNonce": 1515557315, + "isDeleted": false, + "id": "YBAXEJLMBkbdsj-9oEQKJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13620.269060954219, + "y": 2269.154768446157, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 280.1000061035156, + "height": 70, + "seed": 1056068877, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Finality Round Votes\nand EOTS commits", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4C_kRR9PuZj7jJ5ntwVfz", + "originalText": "Finality Round Votes\nand EOTS commits", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "arrow", + "version": 1369, + "versionNonce": 1289199597, + "isDeleted": false, + "id": "ByxqHImFIZUnMJs-EiM9L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13812.275096950993, + "y": 2390.014760399173, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.2359759580067475, + "height": 288.15101085679635, + "seed": 1075003501, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "fLgfxPQD7EMPAdFRAQAei" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gMWY7fP5rBveUymQJJJE_", + "focus": 0.8693215366473958, + "gap": 22.152538642470063 + }, + "endBinding": { + "elementId": "EK_xusAsckpkIcS-MXJEd", + "focus": -0.8663447593994864, + "gap": 18.794583518460172 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.2359759580067475, + -288.15101085679635 + ] + ] + }, + { + "type": "text", + "version": 57, + "versionNonce": 1172595043, + "isDeleted": false, + "id": "fLgfxPQD7EMPAdFRAQAei", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 14063.484224706346, + "y": 2279.849029426974, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 232.39999389648438, + "height": 35, + "seed": 994674275, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Withdraw Reward", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ByxqHImFIZUnMJs-EiM9L", + "originalText": "Withdraw Reward", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 357, + "versionNonce": 1108810317, + "isDeleted": false, + "id": "a1_3jaimGqTutIIO3YJT5", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11220.22142839115, + "y": 742.0761538254686, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 2572.5693492510527, + "height": 187.83839692944207, + "seed": 649066307, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "mfQpANsypEbrVN4CpqxpA", + "type": "arrow" + }, + { + "id": "yqd26byPLQs14kelxJ1-G", + "type": "arrow" + }, + { + "id": "a-2O3cQmmsLXw3Bxai_qN", + "type": "arrow" + }, + { + "id": "IyuMVWi6LT-PazE_jpikJ", + "type": "arrow" + }, + { + "id": "7wvuIjAIlJ-I_bJox-PJW", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 46, + "versionNonce": 1647852803, + "isDeleted": false, + "id": "1EJ_sCjHoobEzX1BnOrBx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12295.528193422015, + "y": 829.1896132710069, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.0999984741211, + "height": 45, + "seed": 926608621, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Bitcoin", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Bitcoin", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 496, + "versionNonce": 708331693, + "isDeleted": false, + "id": "fmxh-Xmm15Jabfus_Ij8h", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11203.88765474511, + "y": 1150.4204949764294, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 337.5646553514599, + "height": 196.00528375246134, + "seed": 1426440227, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "mfQpANsypEbrVN4CpqxpA", + "type": "arrow" + }, + { + "id": "XB9cK1H8fLx9OrleB1iOc", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 1377, + "versionNonce": 1791992995, + "isDeleted": false, + "id": "mfQpANsypEbrVN4CpqxpA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11400.145272730319, + "y": 943.526028793276, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.4503503342602926, + "height": 182.3938057140956, + "seed": 1171976963, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "a1_3jaimGqTutIIO3YJT5", + "focus": 0.8589578232837415, + "gap": 13.611478038365362 + }, + "endBinding": { + "elementId": "fmxh-Xmm15Jabfus_Ij8h", + "focus": 0.147738763176863, + "gap": 24.50066046905772 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -1.4503503342602926, + 182.3938057140956 + ] + ] + }, + { + "type": "arrow", + "version": 1512, + "versionNonce": 652520205, + "isDeleted": false, + "id": "XB9cK1H8fLx9OrleB1iOc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11390.84266061509, + "y": 1357.3149611595832, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.216259688962964, + "height": 123.69450834866484, + "seed": 631603661, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "fmxh-Xmm15Jabfus_Ij8h", + "focus": -0.10868694285339095, + "gap": 10.889182430692472 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.216259688962964, + 123.69450834866484 + ] + ] + }, + { + "type": "rectangle", + "version": 561, + "versionNonce": 660150339, + "isDeleted": false, + "id": "673a9lNedA3G2dhKONQ6_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11756.652547696174, + "y": 1153.1427905841026, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 348.45383778215194, + "height": 185.9986790618848, + "seed": 1752415309, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "yqd26byPLQs14kelxJ1-G", + "type": "arrow" + }, + { + "id": "PIo2X8_qG0ipchEme9mOU", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 761, + "versionNonce": 1754644845, + "isDeleted": false, + "id": "yqd26byPLQs14kelxJ1-G", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11906.783749884315, + "y": 1139.5313125457374, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.3638562037322117, + "height": 190.5606925371153, + "seed": 1426207373, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "673a9lNedA3G2dhKONQ6_", + "focus": -0.126308964431348, + "gap": 13.611478038365135 + }, + "endBinding": { + "elementId": "a1_3jaimGqTutIIO3YJT5", + "focus": 0.4698039393782825, + "gap": 19.05606925371137 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.3638562037322117, + -190.5606925371153 + ] + ] + }, + { + "type": "arrow", + "version": 1034, + "versionNonce": 2075812291, + "isDeleted": false, + "id": "PIo2X8_qG0ipchEme9mOU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11915.562640100825, + "y": 1489.5379580336917, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.36581632484558213, + "height": 143.66011054117143, + "seed": 999483395, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783800188, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": -0.23279561597819842, + "gap": 24.91817898414638 + }, + "endBinding": { + "elementId": "673a9lNedA3G2dhKONQ6_", + "focus": 0.08424125039419571, + "gap": 6.736377846532946 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.36581632484558213, + -143.66011054117143 + ] + ] + }, + { + "type": "arrow", + "version": 1114, + "versionNonce": 1419054029, + "isDeleted": false, + "id": "a-2O3cQmmsLXw3Bxai_qN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12451.504013980817, + "y": 938.8605577954424, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.5967360645390727, + "height": 198.6444867575285, + "seed": 214669677, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "a1_3jaimGqTutIIO3YJT5", + "focus": 0.0429912382060799, + "gap": 8.946007040531754 + }, + "endBinding": { + "elementId": "1W8DJ9txG4KCNn-8JrThS", + "focus": -0.08309477489031791, + "gap": 14.950029373862549 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.5967360645390727, + 198.6444867575285 + ] + ] + }, + { + "type": "arrow", + "version": 1115, + "versionNonce": 1348174403, + "isDeleted": false, + "id": "OMRiiKcWvoZZ9P2ZIgA2p", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12460.376285434088, + "y": 1359.7181546722893, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.386867845279994, + "height": 138.94510037598502, + "seed": 745408451, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783805326, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ZdXGL4Hg2PSzAEO98aq3F", + "focus": -0.052520323556520976, + "gap": 23.499022867740905 + }, + "endBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": 0.08546511481810001, + "gap": 15.792881969563723 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.386867845279994, + 138.94510037598502 + ] + ] + }, + { + "type": "rectangle", + "version": 937, + "versionNonce": 1514322723, + "isDeleted": false, + "id": "RPd3Ts2qYHajodT4DqAiv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12846.853117088702, + "y": 1146.761544911934, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 402.1646011736996, + "height": 184.51025311618514, + "seed": 1539936397, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "7hrt9Q8ALp2hgi-CH7RhI", + "type": "arrow" + }, + { + "id": "IyuMVWi6LT-PazE_jpikJ", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 1301, + "versionNonce": 630366947, + "isDeleted": false, + "id": "7hrt9Q8ALp2hgi-CH7RhI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13049.96504755975, + "y": 1498.6632550482743, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.19527532206120668, + "height": 153.88616803142645, + "seed": 13301133, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783815330, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": 0.42118144300698207, + "gap": 15.792881969563723 + }, + "endBinding": { + "elementId": "7YBNHHpVyngxousOJEXUu", + "focus": -0.00745054484076321, + "gap": 22.37247485594844 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.19527532206120668, + -153.88616803142645 + ] + ] + }, + { + "type": "arrow", + "version": 1221, + "versionNonce": 254971491, + "isDeleted": false, + "id": "IyuMVWi6LT-PazE_jpikJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13038.762181968208, + "y": 1130.9031564859067, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.1934308415275154, + "height": 181.70134749614238, + "seed": 1287719491, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "RPd3Ts2qYHajodT4DqAiv", + "focus": -0.03588056385821184, + "gap": 15.858388426027204 + }, + "endBinding": { + "elementId": "a1_3jaimGqTutIIO3YJT5", + "focus": -0.4092387389518107, + "gap": 19.287258234853653 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.1934308415275154, + -181.70134749614238 + ] + ] + }, + { + "type": "arrow", + "version": 1580, + "versionNonce": 500055373, + "isDeleted": false, + "id": "7wvuIjAIlJ-I_bJox-PJW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13618.769431129636, + "y": 947.027444618464, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.6850431259772449, + "height": 170.84265905543248, + "seed": 1588284077, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "a1_3jaimGqTutIIO3YJT5", + "gap": 17.112893863553268, + "focus": -0.8641123108830687 + }, + "endBinding": { + "elementId": "DjV5UB4NwXHIkAXHRfeJA", + "gap": 25.677323001460536, + "focus": -0.014279542779406101 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.6850431259772449, + 170.84265905543248 + ] + ] + }, + { + "type": "arrow", + "version": 1685, + "versionNonce": 857872493, + "isDeleted": false, + "id": "BWjQ8wJWdXaye_ajaXVvj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13623.465766647503, + "y": 1346.4259722215186, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 2.3896433100544527, + "height": 139.7898599610403, + "seed": 1682973635, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783829179, + "link": null, + "locked": false, + "startBinding": { + "elementId": "DjV5UB4NwXHIkAXHRfeJA", + "focus": 0.004749821888850901, + "gap": 16.72451984765621 + }, + "endBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": 0.7541251637875088, + "gap": 28.240304835279176 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 2.3896433100544527, + 139.7898599610403 + ] + ] + }, + { + "type": "rectangle", + "version": 1085, + "versionNonce": 1762855853, + "isDeleted": false, + "id": "4Yrr-euaC9DdSkW5y8oyN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11238.362441030637, + "y": 2911.4268564867584, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 348.45383778215194, + "height": 235, + "seed": 1053963971, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "88f60GDXQNihrbx7Rd4tO", + "type": "arrow" + }, + { + "id": "kpT676I1UQJTahGMjR2KY", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1025, + "versionNonce": 1668222477, + "isDeleted": false, + "id": "qpE4b-DBvZhuDy_uFDRU-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10602.608259175538, + "y": 2909.8906508026707, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 348.45383778215194, + "height": 235, + "seed": 552851363, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "88f60GDXQNihrbx7Rd4tO", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 1411, + "versionNonce": 198952045, + "isDeleted": false, + "id": "88f60GDXQNihrbx7Rd4tO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10958.787694910654, + "y": 3020.2730198147474, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 260.95996573632874, + "height": 0.3117425560913034, + "seed": 1959568003, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "mWWsvhW90Myb-JBP-3vCi" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qpE4b-DBvZhuDy_uFDRU-", + "focus": -0.04687499999998858, + "gap": 7.725597952964108 + }, + "endBinding": { + "elementId": "4Yrr-euaC9DdSkW5y8oyN", + "focus": 0.07812499999995659, + "gap": 18.614780383652487 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 260.95996573632874, + -0.3117425560913034 + ] + ] + }, + { + "type": "text", + "version": 54, + "versionNonce": 67290339, + "isDeleted": false, + "id": "mWWsvhW90Myb-JBP-3vCi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 10953.944506448326, + "y": 3470.4558653791087, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 184.10000610351562, + "height": 45, + "seed": 222508877, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Signatures", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "88f60GDXQNihrbx7Rd4tO", + "originalText": "Signatures", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "arrow", + "version": 1549, + "versionNonce": 1176678093, + "isDeleted": false, + "id": "kpT676I1UQJTahGMjR2KY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11396.227121735248, + "y": 2892.2100867640706, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.682493292615618, + "height": 197.62170939324778, + "seed": 33091277, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "4Yrr-euaC9DdSkW5y8oyN", + "focus": -0.09639886068542863, + "gap": 19.216769722687786 + }, + "endBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": 0.5288712224987059, + "gap": 17.67248434620251 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.682493292615618, + -197.62170939324778 + ] + ] + }, + { + "type": "rectangle", + "version": 960, + "versionNonce": 633221251, + "isDeleted": false, + "id": "PNGdxsolpFZkdFBDEJXv3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11751.04844200363, + "y": 2904.6389201247093, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 348.45383778215194, + "height": 235, + "seed": 1375695139, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "8E8bIA1TLsDkEkmLGooFY", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 881, + "versionNonce": 1078667299, + "isDeleted": false, + "id": "8E8bIA1TLsDkEkmLGooFY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11913.304049881848, + "y": 2884.465899306031, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.4217447706050734, + "height": 182.87469028483883, + "seed": 838428973, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "PNGdxsolpFZkdFBDEJXv3", + "focus": -0.07446390437993745, + "gap": 20.173020818678197 + }, + "endBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": 0.22927907638047093, + "gap": 24.675315996571953 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.4217447706050734, + -182.87469028483883 + ] + ] + }, + { + "type": "rectangle", + "version": 246, + "versionNonce": 460660621, + "isDeleted": false, + "id": "Dl-HTanqJ1Hckc2GzBlri", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12488.663318115425, + "y": 3184.6804316677885, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ff8787", + "width": 1505.4294710432077, + "height": 434.88068008259864, + "seed": 568905955, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "7lTqxzE2G6qkRBd-0W_JC", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 224, + "versionNonce": 1934408077, + "isDeleted": false, + "id": "-240uTfhSQUOLibdAW3ww", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13032.334069125285, + "y": 2851.396312359026, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ff8787", + "width": 334.8423597437868, + "height": 145, + "seed": 856193773, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "GFH7lVRLTqVlZnVWPiljy", + "type": "arrow" + }, + { + "id": "7lTqxzE2G6qkRBd-0W_JC", + "type": "arrow" + } + ], + "updated": 1704783845793, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 768, + "versionNonce": 902019939, + "isDeleted": false, + "id": "GFH7lVRLTqVlZnVWPiljy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13200.29273144336, + "y": 2826.895651889968, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 2.0423116889724042, + "height": 132.99212459601404, + "seed": 865456141, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-240uTfhSQUOLibdAW3ww", + "focus": 0.012027720599590914, + "gap": 24.500660469057948 + }, + "endBinding": { + "elementId": "mcq3uHl3gntSG5245aRvO", + "focus": -0.49843662270444483, + "gap": 16.98763426933374 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -2.0423116889724042, + -132.99212459601404 + ] + ] + }, + { + "type": "arrow", + "version": 520, + "versionNonce": 1173512269, + "isDeleted": false, + "id": "7lTqxzE2G6qkRBd-0W_JC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13203.710310763878, + "y": 3009.2894576040644, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.3847324452253815, + "height": 150.8903135946657, + "seed": 409933379, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-240uTfhSQUOLibdAW3ww", + "focus": -0.022298305855725533, + "gap": 12.89314524503834 + }, + "endBinding": { + "elementId": "Dl-HTanqJ1Hckc2GzBlri", + "focus": -0.04867598969964186, + "gap": 24.500660469058403 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.3847324452253815, + 150.8903135946657 + ] + ] + }, + { + "type": "text", + "version": 128, + "versionNonce": 287069955, + "isDeleted": false, + "id": "_8y98_xJch7NLILMKEhCn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 10664.552144282003, + "y": 1903.5825172373818, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 246.06666564941406, + "height": 70, + "seed": 2075553933, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Epoched validator\nset change.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Epoched validator\nset change.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 94, + "versionNonce": 1589300909, + "isDeleted": false, + "id": "wYHEguLvgU82KMiyAfZJJ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 11067.583960803446, + "y": 1893.5825172373818, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 280.1333312988281, + "height": 105, + "seed": 1490476131, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Create and maintain\nBLS multisig as \ncheckpoints.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Create and maintain\nBLS multisig as \ncheckpoints.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 88, + "versionNonce": 438337187, + "isDeleted": false, + "id": "JvfCEyLFHS1pKMGz1meyP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 11451.50762258726, + "y": 1903.693627334623, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 362.73333740234375, + "height": 70, + "seed": 1934013901, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Verify and maintain\nreported BTC checkpoints.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Verify and maintain\nreported BTC checkpoints.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 134, + "versionNonce": 1965453581, + "isDeleted": false, + "id": "4NzZ9SDYkbCZv9tjhYup0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 11930.188771907147, + "y": 1907.1950431598086, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 220.56666564941406, + "height": 70, + "seed": 698171149, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Maintain a BTC\nheader chain.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Maintain a BTC\nheader chain.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 94, + "versionNonce": 729723459, + "isDeleted": false, + "id": "5I-dCP4iyKE0NnGgrwMXP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 12319.448498565636, + "y": 1891.3792858133634, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 318.9333190917969, + "height": 105, + "seed": 683909869, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Maintain the consumer\nzone headers and their\nBTC Timestamps.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Maintain the consumer\nzone headers and their\nBTC Timestamps.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 221, + "versionNonce": 1175810531, + "isDeleted": false, + "id": "gtGLLlyfysHcg3f6taCDq", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 90, + "angle": 0, + "x": 12836.048160600903, + "y": 1891.8793279155798, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 259.6333312988281, + "height": 105, + "seed": 1947919235, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Verify and maintain\nBTC stake and\nFinality Providers.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Verify and maintain\nBTC stake and\nFinality Providers.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 225, + "versionNonce": 1704808909, + "isDeleted": false, + "id": "vBcHGfi5-K9nKkqyhznI0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 90, + "angle": 0, + "x": 13228.70469666267, + "y": 1885.0665706283746, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 313.76666259765625, + "height": 105, + "seed": 1759651971, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Verify and maintain\nfinality votes\nand EOTS randomness.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Verify and maintain\nfinality votes\nand EOTS randomness.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 132, + "versionNonce": 1257454979, + "isDeleted": false, + "id": "d7jrXxnu9PHk_MGUeW8xK", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 90, + "angle": 0, + "x": 13679.341546413047, + "y": 1884.566528526157, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 218.5, + "height": 105, + "seed": 669907437, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Distribution of\nnetwork support\nrewards.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Distribution of\nnetwork support\nrewards.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 66, + "versionNonce": 2131464237, + "isDeleted": false, + "id": "fdIyxAx6eQhS7u-aK6ckn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11211.77038288783, + "y": 1167.7503926873162, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 313.4666748046875, + "height": 45, + "seed": 730865635, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Vigilante Reporter", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Vigilante Reporter", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 81, + "versionNonce": 1968608547, + "isDeleted": false, + "id": "T3pfRIYcYPL6bFv5zCqZS", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 11228.841464847414, + "y": 1249.8753662004258, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 286.6333312988281, + "height": 70, + "seed": 1488152515, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Report BTC headers\nand checkpoints.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Report BTC headers\nand checkpoints.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 109, + "versionNonce": 551748237, + "isDeleted": false, + "id": "vrYH93HbDN6yP8Fy_dGmB", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11766.58320148878, + "y": 1171.4048028939699, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 327.8999938964844, + "height": 45, + "seed": 1306485613, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Vigilante Submitter", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Vigilante Submitter", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 99, + "versionNonce": 346945731, + "isDeleted": false, + "id": "3ub_r5XSJBHCTWXlFecrP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 11797.574376497836, + "y": 1245.838596820386, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 251.3000030517578, + "height": 70, + "seed": 907125251, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Submit checkpoints\nto BTC.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Submit checkpoints\nto BTC.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "rectangle", + "version": 602, + "versionNonce": 1339893997, + "isDeleted": false, + "id": "1W8DJ9txG4KCNn-8JrThS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12276.206992082845, + "y": 1152.4550739268336, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 384.4263330666291, + "height": 185.9986790618848, + "seed": 2027432301, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "a-2O3cQmmsLXw3Bxai_qN", + "type": "arrow" + }, + { + "id": "OMRiiKcWvoZZ9P2ZIgA2p", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 151, + "versionNonce": 110618723, + "isDeleted": false, + "id": "lZCmgY7bzBcYE7Xa_wM8O", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12283.33590030968, + "y": 1168.7117991180526, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 367.6666564941406, + "height": 45, + "seed": 497840493, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Checkpointing Monitor", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Checkpointing Monitor", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 155, + "versionNonce": 1032029005, + "isDeleted": false, + "id": "ZdXGL4Hg2PSzAEO98aq3F", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 12318.534925132479, + "y": 1231.2191318045484, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 268.0333251953125, + "height": 105, + "seed": 1145697667, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "OMRiiKcWvoZZ9P2ZIgA2p", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Monitor Consistency\nbetween Babylon\nand BTC.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Monitor Consistency\nbetween Babylon\nand BTC.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "text", + "version": 157, + "versionNonce": 1893917699, + "isDeleted": false, + "id": "_AjAQ5ECzKaDvH9mnCcJY", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12866.792928280334, + "y": 1161.362933380697, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 368.73333740234375, + "height": 45, + "seed": 2105201475, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC Staking Monitor", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Staking Monitor", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 253, + "versionNonce": 774869421, + "isDeleted": false, + "id": "7YBNHHpVyngxousOJEXUu", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 12880.959888335641, + "y": 1217.4046121608994, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 334.9333190917969, + "height": 105, + "seed": 1865156237, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "7hrt9Q8ALp2hgi-CH7RhI", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Slash Finality Round\noffenders and\nreport stake unbondings.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Slash Finality Round\noffenders and\nreport stake unbondings.", + "lineHeight": 1.25, + "baseline": 95 + }, + { + "type": "rectangle", + "version": 819, + "versionNonce": 1254489923, + "isDeleted": false, + "id": "DjV5UB4NwXHIkAXHRfeJA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13448.196945413038, + "y": 1143.547426675357, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 348.45383778215194, + "height": 186.15402569850542, + "seed": 1062002499, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "BWjQ8wJWdXaye_ajaXVvj", + "type": "arrow" + }, + { + "id": "7wvuIjAIlJ-I_bJox-PJW", + "type": "arrow" + } + ], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 53, + "versionNonce": 739166829, + "isDeleted": false, + "id": "X7yUfzjIr-3NOFjM0jc08", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13507.80436395257, + "y": 1175.793060282003, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 215.23333740234375, + "height": 45, + "seed": 1141714765, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "BTC Staker", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "BTC Staker", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "text", + "version": 89, + "versionNonce": 2028674787, + "isDeleted": false, + "id": "hsSqnNpDaD9_WfjllVJRX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 13495.305779777755, + "y": 1238.3142976597876, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 247.23333740234375, + "height": 70, + "seed": 1974030253, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Stake Bitcoin and\ncollect rewards.", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Stake Bitcoin and\ncollect rewards.", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 114, + "versionNonce": 1245242413, + "isDeleted": false, + "id": "xRSksN_qbcyeHx1gN6uiH", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13103.642533820894, + "y": 3490.2289207297235, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 261, + "height": 45, + "seed": 1929764173, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783837066, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Consumer Zone", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Consumer Zone", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 123, + "versionNonce": 1351305859, + "isDeleted": false, + "id": "caDZYXP-6Bzkxb4Eb4_EA", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "angle": 0, + "x": 12568.87352703562, + "y": 3225.619902190428, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 1376.0564192979637, + "height": 154.06229630816915, + "seed": 1415937955, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 96, + "versionNonce": 1216795437, + "isDeleted": false, + "id": "pWxJ8IrgHfJGgeLDhzpEB", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 13087.295359367748, + "y": 3278.1411395682126, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 307.70001220703125, + "height": 45, + "seed": 772957283, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Babylon Contract", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Babylon Contract", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "image", + "version": 227, + "versionNonce": 107282979, + "isDeleted": false, + "id": "W1XRgLLBnKZdGnv-V4FVQ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12446.625125189941, + "y": 803.3171293101589, + "strokeColor": "transparent", + "backgroundColor": "#e7f5ff", + "width": 79.1786076828552, + "height": 79.1786076828552, + "seed": 1432549997, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "status": "saved", + "fileId": "fa53842a29ce0961cab2326a60a37b9ba18bfb84", + "scale": [ + 1, + 1 + ] + }, + { + "type": "image", + "version": 89, + "versionNonce": 1073603981, + "isDeleted": false, + "id": "_iRFSxYHXSKt6--dzrpBm", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 12239.371692481705, + "y": 1561.9544643531679, + "strokeColor": "transparent", + "backgroundColor": "#e7f5ff", + "width": 79.25400556831883, + "height": 81.51840572741365, + "seed": 847206925, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1704783770352, + "link": null, + "locked": false, + "status": "saved", + "fileId": "8ec1dc6981e6ee38b87d2c4cc93ce1a5a287cc35", + "scale": [ + 1, + 1 + ] + }, + { + "id": "_evFNTW7bn-AoKjXNN_NZ", + "type": "text", + "x": 10645.896165817512, + "y": 2921.7534438548937, + "width": 264.79998779296875, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 2013581901, + "version": 53, + "versionNonce": 799255533, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "EOTS Manager", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 32, + "containerId": null, + "originalText": "EOTS Manager", + "lineHeight": 1.25 + }, + { + "id": "Hch1PSwsJWUE_P6X4aCQ5", + "type": "text", + "x": 10648.920491369734, + "y": 3014.992462144717, + "width": 220.3333282470703, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1400250115, + "version": 47, + "versionNonce": 1320097123, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "Generate EOTS\nrandomness.", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 60, + "containerId": null, + "originalText": "Generate EOTS\nrandomness.", + "lineHeight": 1.25 + }, + { + "id": "z7jqVmJirhSNfddWdOTE_", + "type": "text", + "x": 11270.409958543934, + "y": 2923.1564452618036, + "width": 283.5666809082031, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1849965293, + "version": 57, + "versionNonce": 125937229, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "Finality Provider", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 32, + "containerId": null, + "originalText": "Finality Provider", + "lineHeight": 1.25 + }, + { + "id": "oRk1lQcXcsd7oCGeUnYLW", + "type": "text", + "x": 11244.376633348624, + "y": 3013.1939628481723, + "width": 335.6333312988281, + "height": 105, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 567029187, + "version": 49, + "versionNonce": 1737257219, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "Manage Finality Provider\nand Vote in\nfinality round.", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 95, + "containerId": null, + "originalText": "Manage Finality Provider\nand Vote in\nfinality round.", + "lineHeight": 1.25 + }, + { + "id": "iehFyWIhnoh5DujUNT9sS", + "type": "text", + "x": 11783.98191084734, + "y": 3014.5969642550817, + "width": 274.5, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1364488867, + "version": 38, + "versionNonce": 2054247597, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "Enforce transaction\nformatting.", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 60, + "containerId": null, + "originalText": "Enforce transaction\nformatting.", + "lineHeight": 1.25 + }, + { + "id": "wlTg6sWMPMpIH0PwhrKJC", + "type": "text", + "x": 11760.484918357764, + "y": 2916.7534438548937, + "width": 334.29998779296875, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 137649197, + "version": 58, + "versionNonce": 528308387, + "isDeleted": false, + "boundElements": null, + "updated": 1704783770352, + "link": null, + "locked": false, + "text": "Covenant Emulator", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 32, + "containerId": null, + "originalText": "Covenant Emulator", + "lineHeight": 1.25 + }, + { + "id": "9BopJYEF94m4kH0sJ-y9i", + "type": "text", + "x": 13096.717027283268, + "y": 2867.3279318961627, + "width": 216.63333129882812, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1814788685, + "version": 46, + "versionNonce": 104337901, + "isDeleted": false, + "boundElements": null, + "updated": 1704783858059, + "link": null, + "locked": false, + "text": "IBC Relayer", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 32, + "containerId": null, + "originalText": "IBC Relayer", + "lineHeight": 1.25 + }, + { + "id": "CmwsicIXUm_k2K1UjBN4A", + "type": "text", + "x": 13079.300363159731, + "y": 2936.3579459652583, + "width": 251.46665954589844, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 80, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1470380717, + "version": 28, + "versionNonce": 783533357, + "isDeleted": false, + "boundElements": null, + "updated": 1704783874822, + "link": null, + "locked": false, + "text": "Share chain state", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 25, + "containerId": null, + "originalText": "Share chain state", + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "fa53842a29ce0961cab2326a60a37b9ba18bfb84": { + "mimeType": "image/png", + "id": "fa53842a29ce0961cab2326a60a37b9ba18bfb84", + "dataURL": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLAAAASwCAYAAADrIbPPAAAgAElEQVR4XuzdB7weZZX48XOemffeFCAkdERSSGEJgUAAidRA6ISVFlIAQRS7/nVVdHUX1HXtDSy7FDdCGqEpQUCl9xYggRDS6TWE9HLfmef854agMd4kt7xlyu9+Pi6smXmec77nuTH3ZOa8KnwhgAACCCCAAAI5ELCr9+kq26/qIqvd1lKWrUV8Z4n9VlKKt46bGjoH4dqtojjYxrmws0RxVwujbt6Czs6ki5h0E1XXzGBmWzknpeZ/96YNKq7r+zwqcff3/91Eu3jzjZujc84tVTO/qWuSX/AqwdLmX0/WXpr8H5/EtCL5RzlWWR2YrfFiZSfBCnFlr1G47lopuSVifq2JWyY+XhaUOi1JblkurmmZxKXl0umDy/XUe5bkoKykgAACCCCAAAIIrBNQHBBAAAEEEEAAgTQJ2O0De8jqtd3La32PUux7xL7UIyjF3X0c9BBp6hGr62Ei3V0sPUQt+e+0u6r22FIzKU051iqW5gZa0j5LGlt+eeK0TE2Tf9q7Iu5d0+htJ7rIJFwUhLZIgoa3pKnpbem54yL98MOraxUj+yCAAAIIIIAAAq0RoIHVGiWuQQABBBBAAIEOCdjUEV0kmrVL1BTsHMZ+5+TpoV0i8Ts6F3zAYr+TOts5eRppFzXZ0ZuFHdqMmzsskDyKttKLvpM8ffamqixKnkRbFGh5kXhZ5BoaXxUNXksueVVkh1d15LT3ngrjCwEEEEAAAQQQqKIADawq4rI0AggggAACRRCwOw7aTpa8ubtEnXb3se+ZPCXVMza3W/KHjF3EyY7JPz+QvBe3VREsiphjc7PLVF5O3sB83Xv3ikr0auiSBlcYvSK+9Lo497KcOae5ERYV0YecEUAAAQQQQKAyAjSwKuPIKggggAACCORSwJL3y+S6IbuKLN89jnwvjcu7W2C7i5V2N4t7Jn+Q6Jk8OfW3GVG5RCCpDgskM8XiZGbYm8mcsReSGWMLkyftFiSvfS4MwqYFEjcukFHzX00aXJucFdbhAFgAAQQQQAABBDIvQAMr8yUkAQQQQAABBDomYE9cWJKXHu0dr1zeT0Ppb5H0S56cSv7j+yYjxnfjlb6O+XL3lgWcuqZkrtmLqpY0tHSBiS0MQ1koDZ0WiNtpAQPpt2zIFQgggAACCORdgAZW3itMfggggAACCCQC656k+kPfnlK2fskn9PUtq+/v4uTfneuX/GpPmlQckzQLBKrveonnmgazAhc871x5trhtZkmvofP1gMvLaY6d2BBAAAEEEECgMgI0sCrjyCoIIIAAAgikQsBmXtIgs67fsylavZfz0d4qulfy6tZeyafP9U4+pa8hFUESBAIVEkie3Cpb8tSWij2XfNribNXS7MCVZknQ+XmGy1cImWUQQAABBBBIiQANrJQUgjAQQAABBBBoi8C6J6qu3buf2MqBkQ/2Tn6OH2imA5NGVT+epmqLJNfmVSBp3r6uTp8XjWYHGj4TaWl6uPXbz+iJi5flNWfyQgABBBBAIM8CNLDyXF1yQwABBBDIhYBN2uODsfj91MJBscR7Syx7JUOx9+SJqlyUlyRqKJB83yTjtcKFSVNrulk4IyyVp0vQOF1Om7MwGSKfjOHiCwEEEEAAAQTSKkADK62VIS4EEEAAgUIK2KQBu8auaYhFwRAvfkjy8/aByae27VRIDJJGoEYCzuny5A/Fc2IfPBdoNM2VGqbJDqWnddjMFTUKgW0QQAABBBBAYAsCNLA4IggggAACCNRBIHkFMEheAewfy/L9NZb9k+HU+yVh7O+971aHcNgSAQQ2EnAi3tTNN5VpgcpjLggfl84DntQRU1eBhQACCCCAAAK1F6CBVXtzdkQAAQQQKKCA3dR3j3ht+WDv3YdUdYjGfl8v0rWAFKSMQGYFnGrkvT7nnD0mFj/mG7Z+vHTmzGeT1w+jzCZF4AgggAACCGREgAZWRgpFmAgggAAC2RGwq/fpKp3W7OfLyauAoockg3WOSF4D3DE7GRApAgi0VqD5kxBVbUas8mDJu2mijdNk9HPPMVOrtYJchwACCCCAQOsEaGC1zomrEEAAAQQQaFEgeRVQ5YZBA+K1aw4WXXuwj8OhLvADvU9eEeQLAQQKKZA8Zbk4aWA9bhY8Is7dH5Y7PaLnzlhZSAySRgABBBBAoEICNLAqBMkyCCCAAALFELCHhnaOFr51sHPx4eaDD6n4g2Oz7sXIniwRQKA9As2vHprETzrRB11J7pdu4YN63Py32rMW9yCAAAIIIFBUARpYRa08eSOAAAIItErApo7oIqtn7x/F/hD1friIHerNOrXqZi5CAAEENiHgNFzgLXntMGx6QPxWD/LaIUcFAQQQQACBzQvQwOKEIIAAAgggsIGAXXXI1nHXVw7VcnCED+xwNT3Amy+BhAACCFRTQJ2+Zl4eKDn3QDnsdH8yHP6Z5DXEuJp7sjYCCCCAAAJZEqCBlaVqESsCCCCAQMUF7O6BW8lrq5NPB5Th3tmhKnpg0rBqqPhGLIgAAgi0QcA5XSGmj5hzd5hvuqM05qUnGQzfBkAuRQABBBDInQANrNyVlIQQQAABBDYnkAxdD8oTdx+srmH4e68EyhE8YcWZQQCBtAskg+HfTP7gfl8QxHdIabs/6+lPvZj2mIkPAQQQQACBSgrQwKqkJmshgAACCKRSwG7o30fKa4bHcTBcVI/13ndLZaAEhQACCLRSoHmGVjKT7w4Rf0ewXZc79fiZi1t5K5chgAACCCCQSQEaWJksG0EjgAACCGxOwG7tu0PTMjs6ND3Gx/5YE78bYggggEBeBZyTOPlU1CdF4jtDdXfKroc/oMPGrclrvuSFAAIIIFBMARpYxaw7WSOAAAK5ErCZlzREM685VJriY5zKsclrgoO9iMtVkiSDAAIItFIg+X1wVSzurgZxt0qnrW/ldcNWwnEZAggggECqBWhgpbo8BIcAAgggsCkBu+Og7eI33z7KWzDCOX8KrwVyVhBAAIGWBZpfN/QuvqXkdaoMPvc+HXhJE1YIIIAAAghkTYAGVtYqRrwIIIBAgQWaZ1n5NU0jTNzJyeyXI71ZWGAOUkcAAQTaLJA8mroyVnd3QxBNlVJD8nTWvFfavAg3IIAAAgggUAcBGlh1QGdLBBBAAIHWCdjd53WS1+47tOxshMTykaRptXvr7uQqBBBAAIHWCCQ/DDznzE1N5mjdIQOG36sHXF5uzX1cgwACCCCAQK0FaGDVWpz9EEAAAQQ2K2A3HLZLXH79JIvik1VseDLLqitkCCCAAALVF1DRRS7QW0zspqDnzn/VDz+8uvq7sgMCCCCAAAKtE6CB1TonrkIAAQQQqKKATRmyuy+/c6oXPVPNhjKAvYrYLI0AAgi0QsCpro5F72wQvU66fPBmPfWeJa24jUsQQAABBBComgANrKrRsjACCCCAwOYE7Oaevf1yPWVd00rtwz75F8QQQAABBNInkLxeGJvpI07sOucar9PRs19LX5REhAACCCCQdwF+WMh7hckPAQQQSJGATdxrYKRNJ4uPR5jZISkKjVAQQAABBFohkAyB9+r0qeQTYG8JS/EkHblwditu4xIEEEAAAQQ6LEADq8OELIAAAgggsCkBM9HytX0OCMpyundympnvhxYCCCCAQH4EAtWnJbCbkoe0btRRLz+bn8zIBAEEEEAgbQI0sNJWEeJBAAEEciDQNLnPgUEcj47FnSZmPXOQEikggAACCGxBQJ3MdFaa7Lbyk/Vf588DDAEEEEAAgUoK0MCqpCZrIYAAAgUWsEmDe0Wy4iyx+HzzNqDAFKSOAAIIFF4g+SHjOdHwurCTu1pPn7Og8CAAIIAAAgh0WIAGVocJWQABBBAoroBNGrCr92vPZBB7cc8AmSOAAAKbE2iemWWqD68bAK86WccsfBMxBBBAAAEE2iNAA6s9atyDAAIIFFjAbjpyW1n18ilNYmcGYsd7s7DAHKSOAAIIINBKgfc/zTAM4qvF7XCtjpy2tJW3chkCCCCAAAJ8ZDlnAAEEEEBgywL20NDOTS8sHuF80xhVTZpWvnHLd3EFAggggAACLQskT2OtEXG3Jg9oTQ52PWKqDhuX/P98IYAAAgggsGkBnsDidCCAAAIItCjQ/AmCMqXfYd4nM63MTvfetoYKAQQQQACBSguo6BIN4smBNvxeR817pNLrsx4CCCCAQD4EaGDlo45kgQACCFRMwKb0/0AU+7PV+48nT1r1rdjCLIQAAggggMAWBNTpbLFgcui2Gqejn34BMAQQQAABBN4XoIHFWUAAAQQQELv7vE7xa/eOiEXPZa4VBwIBBBBAoN4C7w9/Xzcvq2nbCXrujJX1jon9EUAAAQTqK0ADq77+7I4AAgjUVaBpwu5DxAXnqpexyWuC29U1GDZHAAEEEECgBQHn3FLv3c0l9VfLmAV3qooBhQACCCBQPAEaWMWrORkjgEDBBeyGw3bxa14eGZueL2L7FpyD9BFAAAEEMiSgzs0Rc5PCrcu/11NeXJih0AkVAQQQQKCDAjSwOgjI7QgggEAWBJKB7Ml43N4nSawXivPHey9BFuImRgQQQAABBFoSaH7FMHkO6y/S0PC/wcg5tyRPZUVIIYAAAgjkW4AGVr7rS3YIIFBwAftTr52jZeFHkz/Xf8rH0qvgHKSPAAIIIJBDgeRTDF93ple7cJv/YfB7DgtMSggggMB6ARpYHAUEEEAgZwLJ01YqE/scHYtdKKIfST5JsJSzFEkHAQQQQACBfxJY91SWuruS1+MvD8YsvImnsjgkCCCAQL4EaGDlq55kgwACBRawm47cVtYuGBlF7ovJdNu9CkxB6ggggAACBRdQp6+5WK9xjd1/oyOnvVRwDtJHAAEEciFAAysXZSQJBBAoskDzJwk6KSVPW/lzvFnnIluQOwIIIIAAAhsKbPRU1o3JU1kxQggggAAC2RSggZXNuhE1AggUXMBu7bGNLOn+0UjiT5qXgQXnIH0EEEAAAQS2KOA0XKCBXOG62+/0uPlvbfEGLkAAAQQQSJUADaxUlYNgEEAAgc0L2E199/Ar/Ce82idNbFu8EEAAAQQQQKBtAk5dUzIn649BEPxMR817pG13czUCCCCAQL0EaGDVS559EUAAgTYI2JT+h8bl8hdE7TTvJWjDrVyKAAIIIIAAApsQCJxOc+YulTHzJzL0nWOCAAIIpFuABla660N0CCBQYAGbe0KjPDbnrEjsK2Z+UIEpSB0BBBBAAIGqCjQPfRcLrgh36nGZDn/snapuxuIIIIAAAu0SoIHVLjZuQgABBKonYDcctku05vVPisWfTV4T3L56O7EyAggggAACCGwo4FTXeAuuK4Vrf6SjXn4WHQQQQACB9AjQwEpPLYgEAQQKLtD8aYJiDV906kd586WCc5A+AggggAACdRVQ1QeTH5Z+GYxZyKcX1rUSbI4AAgi8J0ADi5OAAAII1FHATMJ4fL8zTf2XTOID6xgKWyOAAAIIIIBACwLq3JzQ9DKJu/6fnjtjJUgIIIAAAvURoIFVH3d2RQCBggv8bb6V2jfN+/4F5yB9BBBAAAEEUi/gRJf5QMaVGnb7gZ5+/+upD5gAEUAAgZwJ0MDKWUFJBwEE0i1gt/bYxr+7zfne9KJkvtUu6Y6W6BBAAAEEEEBgYwGnbq03N6XUpet/6WnT5yCEAAIIIFAbARpYtXFmFwQQKLiA3bBfT79q6actkE9577sVnIP0EUAAAQQQyLyAE/GxulsbJPyujp3zWOYTIgEEEEAg5QI0sFJeIMJDAIFsC9j4PQeVJfoqg9mzXUeiRwABBBBAYHMC6wa+h/LDYOTCW1TF0EIAAQQQqLwADazKm7IiAgggIDal/6FN5eiiQP1J3vOBGRwJBBBAAAEEiiGg00vqfiZj5k9MGllRMXImSwQQQKA2AjSwauPMLgggUACB5BMFNZ7S/18tLn/dvH2oACmTIgIIIIAAAgi0IJDMyZofiPup9N5hnH744dUgIYAAAgh0XIAGVscNWQEBBAou8F7jqvfJGsvFsbchBecgfQQQQAABBBBYL5C8Wvi2SPCbsPvin+mJi5cBgwACCCDQfgEaWO23404EECi4QNK4cknj6iSN5Nux2X4F5yB9BBBAAAEEENiEgIouEg1+HZa6/VxHTlsKFAIIIIBA2wVoYLXdjDsQQKDgAusaVxN7n25q3zYv/1JwDtJHAAEEEEAAgVYKJE9kvZM8kfWrsMtuv9BT71nSytu4DAEEEEAgEaCBxTFAAAEEWinw98aVfDeZcTWglbdxGQIIIIAAAggg8A8CTnSZmv7Wde/6Qz35mXfhQQABBBDYsgANrC0bcQUCCBRcwJ64sCSz7xwdqX3TvO9fcA7SRwABBBBAAIEKCTinyzXW37gdOv9Ij5+5uELLsgwCCCCQSwEaWLksK0khgEAlBGzmJQ3y5DXnl53/hpj1rMSarIEAAggggAACCGws0PxEljl/WbjjTj/X4Y8lrxnyhQACCCCwsQANLM4EAgggsJFA8qpgEE/qc643u5jGFccDAQQQQAABBGol0PxEVjKm4Nfh9l1/zBNZtVJnHwQQyIoADaysVIo4EUCg6gJJ40qTTxU82Tfpf4n6faq+IRsggAACCCCAAAItCCSNrBXJq4W/dtst+W89cfEykBBAAAEEGOLOGUAAAQTWCdiEPsO92g9ib0MgQQABBBBAAAEE0iCgooskCH4S7nzoL3XYuDVpiIkYEEAAgXoJ8ARWveTZFwEEUiFQnrLnUI1W/7f3emQqAiIIBBBAAAEEEEBgIwEV90oYRt+Vs176napEACGAAAJFFKCBVcSqkzMCCIiN33NQrGv/I5lzdSYcCCCAAAIIIIBAFgTU6ezQ3H/LmPnjk0aWz0LMxIgAAghUSoAGVqUkWQcBBDIhYNcP2jNeu+I7onaG98LvgZmoGkEigAACCCCAwIYCKsGzqv474diF1yGDAAIIFEWAH96KUmnyRKDgAjZlyO7laPF3nNjZSeMqKDgH6SOAAAIIIIBADgSSGVn3hw2lf9eRcx7IQTqkgAACCGxWgAYWBwQBBHItkDSuuvm1737DnH0xeV2wU66TJTkEEEAAAQQQKKSAc3Zr0LD1N/SMZ2cUEoCkEUCgEAI0sApRZpJEoHgC9sSFJZl3+/lRHHzHzHYqngAZI4AAAggggECRBJxI8pB5OKHUeZeL9PT7Xy9S7uSKAALFEKCBVYw6kyUChRKwCX2GR+p/YV4GFipxkkUAAQQQQACBwgs4lVXq3WWuaZfv6QUPLi88CAAIIJAbARpYuSkliSCAQNPkPgdqbD9Jnrg6HA0EEEAAAQQQQKDIAsknFr4WuvjbctZLVyWfWBgX2YLcEUAgHwI0sPJRR7JAoNAC6wa0Ny39L+ei5gHt/L5W6NNA8ggggAACCCCwoUDyB6PnVDt/NRw761ZkEEAAgSwL8INelqtH7AgUXMBuGdTdv7vyIga0F/wgkD4CCCCAAAIIbFHAqbsjaOzybwx63yIVFyCAQEoFaGCltDCEhQACmxZoHtAezbvn8y4ufys2644VAggggAACCCCAwJYFnGokLr4y6FG6WI+b/9aW7+AKBBBAID0CNLDSUwsiQQCBVgjYhF7HRSK/MJM9W3E5lyCAAAIIIIAAAghsJOCcLtfY/8DtNuxnOmzcGoAQQACBLAjQwMpClYgRAQTEbuq7R7wq/r43OxMOBBBAAAEEEEAAgQoIOPdyyfRbOnb+1RVYjSUQQACBqgrQwKoqL4sjgEBHBWzqiC7RsllfSz4856KkedWpo+txPwIIIIAAAggggMA/CiTzse4KtNMXdMxzM7FBAAEE0ipAAyutlSEuBBCQ6NreI3wkl4lZTzgQQAABBBBAAAEEqieQNLHK3tlvS92W/oeeuHhZ9XZiZQQQQKB9AjSw2ufGXQggUEWBpikDB2vTqktN7LAqbsPSCCCAAAIIIIAAAhsJqOjrobqvy5j516iKAYQAAgikRYAGVloqQRwIICB2+8Ae5XdXXezMPuu9BJAggAACCCCAAAII1EdAJXg81OBzOnbOY/WJgF0RQACBfxSggcWJQACBugsknygYyMQ9Ph2J/7aZ9ah7QASAAAIIIIAAAgggIM5JLN79b7B95//Q42cuhgQBBBCopwANrHrqszcCCIjd8C/7R2tW/tbMHQQHAggggAACCCCAQPoEAtV3RezbbswLv0peK4zTFyERIYBAEQRoYBWhyuSIQAoF7KYjt41XvvQ9Ef8pL+JSGCIhIYAAAggggAACCGwgEDidFmvDpxpGz34CGAQQQKDWAjSwai3OfgggsO7TBa2svzHxu8GBAAIIIIAAAgggkB2B5G8dvWhwZdD93a/yaYXZqRuRIpAHARpYeagiOSCQEQH74x5945X+V97bcRkJmTARQAABBBBAAAEEWhB4/9MKdez8qwFCAAEEaiFAA6sWyuyBQMEF7IkLS9Hcu76sPr7Em3UqOAfpI4AAAggggAACuREwdbc0dOr2OT39qRdzkxSJIIBAKgVoYKWyLASFQH4EypP2PlL8imRIu+yZn6zIBAEEEEAAAQQQQOB9AaeyyiT8cTjgqO/pAZeXkUEAAQSqIUADqxqqrIkAAmJ/6rVz+d3wRyLROXAggAACCCCAAAIIFEFAp5fC4FM6at4jRciWHBFAoLYCNLBq681uCOReIHnSSmVCv0/GLv6B975b7hMmQQQQQAABBBBAAIG/CTgncTLl/TelbZd+iyHvHAwEEKikAA2sSmqyFgIFF7Ab+veJ10RXePNHFZyC9BFAAAEEEEAAgUILMOS90OUneQSqIkADqyqsLIpAsQSSp66CaGLvLyW/oXwnGdLeuVjZky0CCCCAAAIIIIDApgSS+Vh/CDp98DN6+v2vo4QAAgh0RIAGVkf0uBcBBMQmf3DvKNarzNxBcCCAAAIIIIAAAgggsLFA8jTWkjCML9JRL12ODgIIINBeARpY7ZXjPgQKLmBPXFiK5t71ZfX+28krg40F5yB9BBBAAAEEEEAAgS0IOJPbgzC4UEfPfxksBBBAoK0CNLDaKsb1CCAgTVMGDg7Kq34Xm+0HBwIIIIAAAggggAACrRVwosuCMP6qnPXSFapirb2P6xBAAAEaWJwBBBBotYA9NLSzn//mxRb4r3gvQatv5EIEEEAAAQQQQAABBDYQUNX7wq3cBfqv8+cBgwACCLRGgAZWa5S4BgEExKb0PzSKyleatwFwIIAAAggggAACCCDQUYFkwPsqc+F3wlHzfpw8jeU7uh73I4BAvgVoYOW7vmSHQIcF7Op9usa64scS+E8lT13xe0aHRVkAAQQQQAABBBBAYEMB5+yeoLHxAj19zgJkEEAAgU0J8MMoZwMBBDYpYJP7Hhx5/3vzvj9MCCCAAAIIIIAAAghUS8CprjYXfDt5GusnydNYcbX2YV0EEMiuAA2s7NaOyBGomoDNPaHRPzL728y6qhoxCyOAAAIIIIAAAgi0IKCi94fb+I/qKS8uBAgBBBDYUIAGFucBAQT+QcAmf3BvH4dX8wmDHAwEEEAAAQQQQACBegg4p8sDF39FR710eT32Z08EEEinAA2sdNaFqBCouYCZBNHkvl9R77/tzTfWPAA2RAABBBBAAAEEEEBgAwGnwW1Bp12T2Vj3vw4MAgggQAOLM4AAAmI39O8TrS6PM7HD4EAAAQQQQAABBBBAIC0CqvqWOvlEOHrhzWmJiTgQQKA+AjSw6uPOrgikQiB56krl2t0/Efvgp97bVqkIiiAQQAABBBBAAAEEEPgngfCa0gcaP6PDZq4ABwEEiilAA6uYdSdrBJKnrg7bJV796hVe/ElwIIAAAggggAACCCCQdgEnsjAw/aies/D+tMdKfAggUHkBGliVN2VFBFIvEI3vN8Yk+nXyyuC2qQ+WABFAAAEEEEAAAQQQWC/gVCNz/gdhv+O+owdcXgYGAQSKI0ADqzi1JlMExK46ZOu40xs/8RZfCAcCCCCAAAIIIIAAApkVMDej1LnLOXrGszMymwOBI4BAmwRoYLWJi4sRyK5A0+Q+B7pYJiafMNg3u1kQOQIIIIAAAggggAAC7wkkT2OtDpx8VUYt/I2qGC4IIJBvARpY+a4v2SEgzYPa/cReXzBxP0qaVw2QIIAAAggggAACCCCQJwFzenPDjjt8TIc/9k6e8iIXBBD4RwEaWJwIBHIsYBN77xTHNs6rHJ/jNEkNAQQQQAABBBBAoOACKu6VsBSM1bPm3ldwCtJHILcCNLByW1oSK7qATeh1XKQyzrzsXHQL8kcAAQQQQAABBBDIv4BzEpuF/xWOmffd5JXCOP8ZkyECxRKggVWsepNtAQTs7vM6ld+494fO7PPeC9/jBag5KSKAAAIIIIAAAgj8XUA1vDcsubE6cs6ruCCAQH4E+OE2P7UkEwTEpuy+V7kpmChi+8KBAAIIIIAAAggggEBRBVR0kYaN54ejnr+lqAbkjUDeBGhg5a2i5FNYAZvc5zPJvKufeLPOhUUgcQQQQAABBBBAAAEE1gskrxSamv3CDT7/6zrwkiZgEEAg2wI0sLJdP6JHQOyqQ7Yud3rtiuTjBs+CAwEEEEAAAQQQQAABBP5RIFB50jVuO0rPeHouNgggkF0BGljZrR2RIyBNUwYOduXV13nzfeFAAAEEEEAAAQQQQACBlgWc0+UiwWeSAe/jMUIAgWwK0MDKZt2IGgGxybtfGMfBL5NXBjvBgQACCCCAAAIIIIAAAlsWCILwd67nDp/TDz+8estXcwUCCKRJgAZWmqpBLAi0QsAeGto5XvjGZUnj6oJWXM4lCCCAAAIIIIAAAgggsIFAoPq06xKcoafOmw8MAghkR4AGVnZqRaQIiF0/aM9ozarrTOK94UAAAQQQQAABBBBAAIH2CTjRZYELP6Zj5t7QvhW4CwEEai1AA6vW4uyHQDsFbFLfc+I4+q0X6drOJbgNAQQQQJtKs+0AACAASURBVAABBBBAAAEE1gs0f0qhV72s1O+Yr+gBl5eBQQCBdAvQwEp3fYgOAbG7z+tUfuPeH0psX4ADAQQQQAABBBBAAAEEKiugqveFnXYbpaff/3plV2Y1BBCopAANrEpqshYCFRawG/ftX165/DpRv0+Fl2Y5BBBAAAEEEEAAAQQQWC+QNLHekpIbXRo5/y5QEEAgnQI0sNJZF6JCQNZOHDAykKYrvbet4UAAAQQQQAABBBBAAIHqCjjVyILSReFZc36uKlbd3VgdAQTaKkADq61iXI9AlQXMJPDj+3zPAv8174Xv0Sp7szwCCCCAAAIIIIAAAhsKmNObGzr3/Kiees8SZBBAID0C/HCcnloQCQJidxy0XfzW25OSp66OgQMBBBBAAAEEEEAAAQTqI6Dq5oaNXc7QM56dUZ8I2BUBBDYWoIHFmUAgJQJNUwYOdk0rb0w+ZbB3SkIiDAQQQAABBBBAAAEECivgRFbGgVzQOPqFawuLQOIIpEiABlaKikEoxRVYO3mPUUEcX+VNuhRXgcwRQAABBBBAAAEEEEiXgHNiGrsfubMXfDOZixWnKzqiQaBYAjSwilVvsk2ZwPvzrmL1F6UsNMJBAAEEEEAAAQQQQACB9QLO5PZgq16jmYvFkUCgfgI0sOpnz84FF1g37+rNRZO9+eEFpyB9BBBAAAEEEEAAAQRSL7BuLlYp+oiOfOm51AdLgAjkUIAGVg6LSkrpF1g37ypeeZOPpVf6oyVCBBBAAAEEEEAAAQQQaBZwTpdLUDo3PGvOHxBBAIHaCtDAqq03uyEg0fh+Y0TLVzDvisOAAAIIIIAAAggggED2BDaYi/WNZC6WZS8DIkYgmwI0sLJZN6LOoEAy7yosT+79U4ntCxkMn5ARQAABBBBAAAEEEEBgQwHVa0vb7P0xHTF1FTAIIFB9ARpY1TdmBwTErjpk66ZOr09U8yfDgQACCCCAAAIIIIAAAjkRMDej1C3+iJ7y4sKcZEQaCKRWgAZWaktDYHkRsBv694lWN01Nni3eKy85kQcCCCCAAAIIIIAAAgi8J6Cq74grnVUaPedOTBBAoHoCNLCqZ8vKCEh50t5Hil95vZltBwcCCCCAAAIIIIAAAgjkU8CpKwdeP6/nzP/ffGZIVgjUX4AGVv1rQAQ5FYgm9b1AvP+NN9+Q0xRJCwEEEEAAAQQQQAABBDYUCPTS0qiFX0qGu3tgEECgsgI0sCrryWoISDKsPfDj+3wvVn8RHAgggAACCCCAAAIIIFAsAafBbUH3d0fpiYuXFStzskWgugI0sKrry+oFE7C7B27V9PqqCertlIKlTroIIIAAAggggAACCCDwvkDzcPdQT9bR818GBQEEKiNAA6syjqyCgNgNfXfza+KbY7P94EAAAQQQQAABBBBAAIFiC6jT18ziUxrGvjSt2BJkj0BlBGhgVcaRVQouUL5mjw+L8zcmw9p3KjgF6SOAAAIIIIAAAggggMB6ASeyMggbx+qo2X8EBQEEOiZAA6tjftyNgNi1/T8aR9H/JsPaG+FAAAEEEEAAAQQQQAABBDYUcE7i5AfvrwRjXvgFMggg0H4BGljtt+NOBKQ8qe9FatH3vRe+lzgPCCCAAAIIIIAAAgggsEkBp3plMOCYz+gBl5dhQgCBtgvwQ3fbzbgDgeZPGgzjiXv82lt8IRwIIIAAAggggAACCCCAQGsEnNO/BmGPM3XktKWtuZ5rEEDg7wI0sDgNCLRRwK46ZOu48bXrvdixbbyVyxFAAAEEEEAAAQQQQKDgAirBs2HDticlTayXCk5B+gi0SYAGVpu4uLjoAnbDYbv4NS/fEpvsX3QL8kcAAQQQQAABBBBAAIH2Cajo62Enf5Ke8eJT7VuBuxAongANrOLVnIzbKWAT9xpY9qtvFbHd27kEtyGAAAIIIIAAAggggAAC6wSS1wlXBC44XUfN+wskCCCwZQEaWFs24goEpDxhj2Fi/kYT2xYOBBBAAAEEEEAAAQQQQKASAk5dk/jgvPCcuZMqsR5rIJBnARpYea4uuVVEwCb1PiP2co0361SRBVkEAQQQQAABBBBAAAEEEFgv4FzzZ0SVvl4aM/dHoCCAwKYFaGBxOhDYjEA8odcXk/81+ZlPnvAFCgEEEEAAAQQQQAABBBComkCgl5ZGLfySqiQ/fvCFAAIbC9DA4kwg0IJA0rQK4gl9LvXiPwMQAggggAACCCCAAAIIIFALARfY9cHOw87RYePW1GI/9kAgSwI0sLJULWKtiYA9NLRz0wtvXqvej6jJhmyCAAIIIIAAAggggAACCKwXSIa7/zVYvevpesGDy0FBAIG/C9DA4jQgsIGATRnSLWpaPDUZ1n4YMAgggAACCCCAAAIIIIBAPQRU3TNhKTxBR855tR77sycCaRSggZXGqhBTXQRsYu+dvMltsdl+dQmATRFAAAEEEEAAAQQQQACB9QIukBeCQI/XkQtng4IAAiI0sDgFCCQCdkP/PvGa6M/efF9AEEAAAQQQQAABBBBAAIE0CKjq2+YaTmwYPfuJNMRDDAjUU4AGVj312TsVAjZxr4GRrPmzef+BVAREEAgggAACCCCAAAIIIIDAeoFkJtaKwPRUHbvgDlAQKLIADawiV5/cxSb0PyiS8q1mth0cCCCAAAIIIIAAAggggEAaBZy6tRKGo8Kz5vwhjfEREwK1EKCBVQtl9kilgE3oMzxWu8l72yqVARIUAggggAACCCCAAAIIILBewKlGgbgLdOz8q0FBoIgCNLCKWHVylmh8vzGi8bhk5lUJDgQQQAABBBBAAAEEEEAgCwLOSSzefzo8+6UrshAvMSJQSQEaWJXUZK1MCNik3p+NY7vUi7hMBEyQCCCAAAIIIIAAAggggMB6gaSJZRbrRaVzFv4YFASKJEADq0jVJlcpT+p7kcXRD6BAAAEEEEAAAQQQQAABBLIsEJj7YXDOgq9nOQdiR6AtAjSw2qLFtZkVMBMtT+79C4ntC5lNgsARQAABBBBAAAEEEEAAgQ0FnF5WGr3wi6piwCCQdwEaWHmvMPklz9cm064m9r7Um30ODgQQQAABBBBAAAEEEEAgVwJOJyRNrPOSJlaUq7xIBoGNBGhgcSRyLZA0rwI/uc+VcezPy3WiJIcAAggggAACCCCAAAKFFTDnpjbscvhIHTZuTWERSDz3AjSwcl/i4iZoT1xYKs/+6zXJI1hnFVeBzBFAAAEEEEAAAQQQQKAIAs7pn4NeO5+qH354dRHyJcfiCdDAKl7NC5GxzbykIX76mkne4tMKkTBJIoAAAggggAACCCCAQOEFVPW+cNcuJ+mwmSsKjwFA7gRoYOWupCRkc09obHr8+Snq7RQ0EEAAAQQQQAABBBBAAIEiCajo/eHaXU/SCx5cXqS8yTX/AjSw8l/jQmVoU0d0iZc/+wfv7ZhCJU6yCCCAAAIIIIAAAggggMB6ATV9IGza9USaWByJPAnQwMpTNQuei129T9c4WHGzN39UwSlIHwEEEEAAAQQQQAABBAouoIF7KOy25AQ9cfGyglOQfk4EaGDlpJBFT8NuOnLbaPWLt5q3oUW3IH8EEEAAAQQQQAABBBBAoFlAnT4RbrPVsXryM+8igkDWBWhgZb2CxC92y6Du0dKlt5u5g+BAAAEEEEAAAQQQQAABBBD4u0DgdJrr0eVYPX7mYlwQyLIADawsV4/Yxe44aLvyG2/fKWL7woEAAggggAACCCCAAAIIIPDPAjyJxanIgwANrDxUsaA5rH9t8K/Ja4MHFJSAtBFAAAEEEEAAAQQQQACBVgkEqk+7bXocoyOmLWrVDVyEQMoEaGClrCCE0zoBmzKkW9S05K8m8YGtu4OrEEAAAQQQQAABBBBAAIGiC+j0Urcew2liFf0cZDN/GljZrFuho6Z5VejykzwCCCCAAAIIIIAAAgh0RMDcjNK23Y+midURRO6thwANrHqos2e7BezWHttEi7v92cQObvci3IgAAggggAACCCCAAAIIFFogeRJr+y5HMdi90Icgc8nTwMpcyYobsF29T9coWH6rmR1eXAUyRwABBBBAAAEEEEAAAQQ6LpAMdn80XL3rMXrBg8s7vhorIFB9ARpY1TdmhwoI2NQRXeLlM/7kvR5ZgeVYAgEEEEAAAQQQQAABBBAovIAG7qFw587H6bCZKwqPAUDqBWhgpb5EBPhe82rmLd77YWgggAACCCCAAAIIIIAAAghUTsCJuzP4wOEn67Bxayq3KishUHkBGliVN2XFCgrYQ0M7xwvfvMWbP6qCy7IUAggggAACCCCAAAIIIIDAegEn+pfgQ3ueov1uWwsKAmkVoIGV1soQl9jcExrjx5+7MXlt8EQ4EEAAAQQQQAABBBBAAAEEqifgVP4QDDh2pB5webl6u7AyAu0XoIHVfjvurKKAzbykIX5y3B+9yvFV3IalEUAAAQQQQAABBBBAAAEE3hdwbnJp9IKzVSUGBYG0CdDASltFiEfMJChP6D1RxEbCgQACCCCAAAIIIIAAAgggUEMBp78vjV74saSJ5Wu4K1shsEUBGlhbJOKCWgokzSuNJ/a+3Jt9vJb7shcCCCCAAAIIIIAAAggggMB7AkEQ/s6NmvfxpIllmCCQFgEaWGmpBHGsE4jH9/t5LOX/BwcCCCCAAAIIIIAAAggggED9BAIp/SI4e+6X6hcBOyPwjwI0sDgRqREoT+h7iVl0cWoCIhAEEEAAAQQQQAABBBBAoMACquG3S2PnXVJgAlJPkQANrBQVo8ih2MQ9Pl/28aVFNiB3BBBAAAEEEEAAAQQQQCBtAkHgvhyMXvDztMVFPMUToIFVvJqnLmObsMe5scbjvBfOY+qqQ0AIIIAAAggggAACCCBQZAHnxIKg4Xw9a87vi+xA7vUXoGFQ/xoUOoLo2v4fkah8XTK0PSw0BMkjgAACCCCAAAIIIIAAAikVcOrKIo0fCcfOujWlIRJWAQRoYBWgyGlN0Sb0GR6L3OLNN6Y1RuJCAAEEEEAAAQQQQAABBBAQcaqrg1LpWB055wE8EKiHAA2seqizp5Sn7DlUo7V/8d62ggMBBBBAAAEEEEAAAQQQQCD9As65pT7sfGTDyJlPpz9aIsybAA2svFU0A/nY9Xvv49euvCc2656BcAkRAQQQQAABBBBAAAEEEEBgvYA6fS3cyh+qp7y4EBQEailAA6uW2uwldkP/PtGa8kNmthMcCCCAAAIIIIAAAggggAAC2RNIZmLND7ZNmlgnvfBG9qIn4qwK0MDKauUyGLdNHbJ9tPzdB837/hkMn5ARQAABBBBAAAEEEEAAAQTWC6i6Z8JuXY/Qk595FxQEaiFAA6sWyuwhNnVEl2jps3ea2MFwIIAAAggggAACCCCAAAIIZF8geZ3w4TDa+hg9d8bK7GdDBmkXoIGV9grlID574sJSPOsvN3uV43OQDikggAACCCCAAAIIIIAAAgisFzCnNzeMXniaqiQfMs8XAtUToIFVPVtWTgTMRP2kPlfF3p8PCAIIIIAAAggggAACCCCAQP4EnAaXh2PnfzJ/mZFRmgRoYKWpGjmMpTy+1/dN5Os5TI2UEEAAAQQQQAABBBBAAAEE1guoK11UGjP3R4AgUC0BGljVkmVdsfH9PlWW8m+hQAABBBBAAAEEEEAAAQQQyLeAc2KBhh/V0fOuyXemZFcvARpY9ZLP+b7RpN6nJO8P3ui9BDlPlfQQQAABBBBAAAEEEEAAAQQSAaeuyVx4Ymn0nDsBQaDSAjSwKi3KemLX9js8jqI/e7NOcCCAAAIIIIAAAggggAACCBRHwDm3NPANh+nZzz9TnKzJtBYCNLBqoVygPWziXgO9rb4/NuteoLRJFQEEEEAAAQQQQAABBBBAYL2Ainsl7OyG6unzXgEFgUoJ0MCqlCTriE3a44NRbA+Z+N3gQAABBBBAAAEEEEAAAQQQKK5AoPKk27XrETps5oriKpB5JQVoYFVSs8Br2d0Dtyq/uuoBEdu3wAykjgACCCCAAAIIIIAAAgggsF7AaXBbMGb+KaoSgYJARwVoYHVUkPuTWe3imib1vkm9nQIHAggggAACCCCAAAIIIIAAAu8LONWrwrELP44IAh0VoIHVUUHul6R59UuJ7QtQIIAAAggggAACCCCAAAIIILCxQOBK3wrGzP0eMgh0RIAGVkf0uDeZe9X7s+XYfgUFAggggAACWREIDvvdulDtzceT/zwotnRGVkInTgQQQAABBDIp4Jwk7+3YOeGoFydkMgGCToUADaxUlCGbQdiEXsfFord4szCbGRA1AggggEDhBFyjlEZOFwka/5a6rV0i9tZ0sbefFnnrUfHvPNLc3iocDQkjgAACCCBQTYHkVcI1Vmo8qjTy+YeruQ9r51eABlZ+a1vVzGzK7nvFUfiQ975bVTdicQQQQAABBCoo4HYaLsHwKza7YnNDS955PmlqPS32xn00tCroz1IIIIAAAsUWUNU3w1KPg3TktJeKLUH27RGggdUetYLfY3/qtXN5iT6STG/vWXAK0kcAAQQQyJhAsM+3xA26oE1R09BqExcXI4AAAgggsFmBpAnxXNhj2VA9cfEyqBBoiwANrLZoca3YQ0M7RwveuMvEDoYDAQQQQACBrAmEw28S3Wlwh8KmodUhPm5GAAEEEEBAnAa3BWPmj1CVGA4EWitAA6u1UlyXPHAlWp7Qq3no3mg4EEAAAQQQyJxAC/OvKpGDrV2avHI4i1cOK4HJGggggAAChREIzP0wOGfB1wuTMIl2WIAGVocJi7NAeXyv7ycjbfkNpjglJ1MEEEAgVwKtmX9ViYT/qaG1+LFkJjx/wVwJW9ZAAAEEEMiXgBN/YXj2S5sfTpmvlMmmAwI0sDqAV6Rbo4l9zk8Gtr/3ueN8IYAAAgggkEGB9sy/qkSaNLQqocgaCCCAAAJ5FHDq1galcLiOnPNAHvMjp8oK0MCqrGcuVytP2XOolpvu9ub//pnjucyUpBBAAAEE8ixQiflXFfEprxJr/pTDNx5771MOeUKrIqwsggACCCCQTYHkkwnfCbsEH9JT583PZgZEXSsBGli1ks7oPnbDYbtEa195wrztmtEUCBsBBBBAAAGRKs2/qggtDa2KMLIIAggggEB2BdTJrDDcbqiOnJYMluQLgZYFaGBxMjYpYHef1yl67a57zdxBMCGAAAIIIJBlAbfT0RIMvzIbKdDQykadiBIBBBBAoKICzuT24OwXTuaTCSvKmqvFaGDlqpyVTaZpfN+rRaJzKrsqqyGAAAIIIFB7gWCfb4ob9PHab1yJHWloVUKRNRBAAAEEMiAQBKWfBKPnfjUDoRJiHQRoYNUBPQtblsf3/zeTpp9kIVZiRAABBBBAYEsC4fAbRXfab0uXZePXaWhlo05EiQACCCDQLoFSqeE8PWvO79t1MzflWoAGVq7L277kbEKf4bHYbd4sbN8K3IUAAggggECKBNI8/6oSTDS0KqHIGggggAACKRFwqquDTp0O1dNnPZmSkAgjJQI0sFJSiLSEYZMG94ripY+b2PZpiYk4EEAAAQQQ6IhApuZfdSTR9+/dsKH19uPiF90v4suVWJk1EEAAAQQQqI2A6oulbXocoCOmLarNhuySBQEaWFmoUo1itLsHbhW9uuZhk3jvGm3JNggggAACCFRdINPzryqhE60WWzRL7I3HxGhoVUKUNRBAAAEEaiDg1N0RjFlwPEPda4CdkS1oYGWkUNUO00w0ntj72uS1wTOrvRfrI4AAAgggUEuB8OgbRHfev5Zbpnova1op8vYMiR78rEj53VTHSnAIIIAAAsUWUBd+vzRm3r8XW4Hs3xeggcVZWCdQntD3ErPoYjgQQAABBBDIlUDe51+1s1i2erFENw5p593chgACCCCAQG0EnBOLpXFU45jZU2qzI7ukWYAGVpqrU6PYbPKAf42jtTd6EVejLdkGAQQQQACBmgi4HY+S4JirarJXljbxL94l8QMXZClkYkUAAQQQKKiAc7oicNFQHfXyswUlIO31AjSwCn4U7PpBe8ZNKx7z3rYuOAXpI4AAAgjkUCDY59/FDfpEDjPrWErx4z8UP+d/OrYIdyOAAAIIIFAjAVU3N+yy+0F66j1LarQl26RQgAZWCotSq5Ds6n26RuGyR83LwFrtyT4IIIAAAgjUUoD5Vy1rR7ecIrb0mVqWgr0QQAABBBDokIAT/UswduGJDHXvEGOmb6aBlenydSz4pgm7/17MnduxVbgbAQQQQACBlAo0z78682mRsFNKA6xPWMy/qo87uyKAAAIIdFxAVb9bGrvwPzu+EitkUYAGVharVoGYbXKfz5Qj/+sKLMUSCCCAAAIIpFKA+Vctl4X5V6k8rgSFAAIIINAKgeah7oFrPFVHzf5jKy7nkpwJ0MDKWUFbk45N6H9QLNF93nxja67nGgQQQAABBLIowPyrlqvG/KssnmZiRgABBBB4X0BFl4SdS0P09DkLUCmWAA2sYtVb7PaBPeJFK59IPnGwd8FSJ10EEEAAgYIJMP+q5YJHt/xrMv9qRsFOA+kigAACCORLQKeX+uw8VD/88Op85UU2mxOggVWg82EmLp64xy3e4hMKlDapIoAAAggUUYD5Vy1W3dYsluiGA5JfsyKeCnJGAAEEEMiRgJPw1+HZ8z6Xo5RIZQsCNLAKdETiSb0ujmO5pEApkyoCCCCAQEEF3I7DJDjmdwXNftNp+5fulvj+j+GCAAIIIIBALgScC88Jx8wbn4tkSGKLAjSwtkiUjwvKk/ofrdb0Z+8lyEdGZIEAAggggMCmBYJB3xC3z4UQbSQQP/4j8XN+iwsCCCCAAAK5EHBOVwSu60E66tlZuUiIJDYrQAOrAAfEJu3xwcj7aWa2QwHSJUUEEEAAAQQkPPp60Z2HILGRAPOvOBIIIIAAAnkTUAmeDbvt9SEdMXVV3nIjn38UoIGV8xNhT1xYimb/9e6keXVIzlMlPQQQQAABBN4TcCUpnZkMKQ87IbKBQN7nX+lWe4p22UX8ogdEfJnaI4AAAggUSMCpXhWOXfjxAqVcyFRpYOW87E0Te18q3j6f8zRJDwEEEEAAgb8JMP+q5cOQ9/lXwb7fFrf3uSLxWrF354m9PV3s9QfEv3l30tBaw3cIAggggEDOBUqlhvP0rDm/z3mahU6PBlaOyx9N3vPk5A9sNydzr6hzjutMaggggAAC/yjA/KuWT0T8+I+T+Ve/ye1xCY+9WXSHQf+c3z81tO5NGlq8ZZLbg0BiCCBQWIHkKaw1QVA6WEfNmV5YhJwnTmMjpwW2G/ruFq2Jn05eHdwupymSFgIIIIAAAi0KMP+q5YMR3fIRsaU5/TN90FVKI59a9/roFr/ipuQJrbkbPKFFQ2uLZlyAAAIIZERAnZsTbrvkQD1x8bKMhEyYbRCggdUGrKxcaiZhNLH3Pcy9ykrFiBMBBBBAoGICzL9qkdLWLpHo+v2TX7OKUadpIbfz8RIc3c5PV/Sx2JIFYm8+vv6VQxpaaaotsSCAAAJtFlC9tmHswlFtvo8bUi9AAyv1JWp7gOVJPb9rsX6r7XdyBwIIIIAAAtkWYP5Vy/XL//yrS5L5Vx+tzOGloVUZR1ZBAAEE6ijgwvAT4ah5V9YxBLauggANrCqg1nNJm7LbEXEU3pnMvQrqGQd7I4AAAgggUA8B5l+1rF7Y+VeVOIQbN7Teui8ZFL+yEiuzBgIIIIBAlQScyMqg09YH6BnPPF+lLVi2DgI0sOqAXq0t7da+O0RLkrlX3nat1h6siwACCCCAQJoFwqOvE935gDSHWJfYcj3/ynWR0lnN868aamNLQ6s2zuyCAAIIdFBA1T0T7nr4QTpsHB9F20HLtNxOAystlehgHMncK22a1PsP6u2UDi7F7QgggAACCGRTgPlXLdYt//OvjkvmX/1P/c4sDa362bMzAgggsAWBIAx+Goya/xWg8iFAAysfdZTy+P7/ZtL0k5ykQxoIIIAAAgi0WcDteKQEx/xfm+/L+w3+5Xskvu/83KYZ7HtxMv/qvPTkt0FDy789TeyNe0TKi9MTH5EggAACBRJwLvn0ktiNCM9Z8KcCpZ3bVGlg5aC0TRN2H+IkfMibr9Gz8zlAIwUEEEAAgdwJMP+q5ZLGT/xE/Oxf567e7ycUHvtH0R32SW9+NLTSWxsiQwCBQgio6lvhtravnvTCG4VIOMdJ0sDKeHHt7oFbRa+veiKZezUg46kQPgIIIIAAAh0SYP5Vy3zRn04VW/J0h2xTe3Ot519VAoKGViUUWQMBBBBok4AzuT04+4UTVZMnsvjKrAANrMyW7r3Am8b3vUYkOjvjaRA+AggggAACHRNonn91RtKkKXXp2Do5uzv/86+OTeZf/W+2q0ZDK9v1I3oEEMiMQKClLwZj516amYAJ9J8EaGBl+FDYtf0/Wi43jctwCoSOAAIIIIBARQSYf9UyY+7nX+3zn+IG5Wy+Fw2tivyewCIIIIDAxgJO3dogCD+ko+ZMRyebAjSwslk3sZt79o6Xuae92DYZTYGwEUAAAQQQqJgA869apsz9/Ktj/iC6474VO0epXMiSP+29O1/szceFofCprBBBIYBAhgSSBshzYbdBB+qIqasyFDahrheggZXBo2AmLprY9y6z6IgMhk/ICCCAAAIIVFyA+Vctk0Z/Oi2Zf/VUxb1TsWAW519VAu79htbbM8SSTzn0r/8lmSnxTiVWZg0EEECgEAIu8P8Tjn7p04VINmdJ0sDKYEHLk/peZHH0gwyGTsgIIIAAAghUXoD5Vy2a2tqlEl2/X/Jr+ZxX63bOwfyrSnw30NCqhCJrIIBAwQRKDXaajnzxpoKlnfl0aWBlrIRNUwYOduXVj3rzDRkLnXARQAABBBCoioDb4QgJjh1XlbWzvKh/+V6J7zsvyylsNvYgj/OvKlCt+Imfip/9qwqsxBIIIIBAfgVUdFHY3QbpSS+8kd8s85cZfKp3tQAAIABJREFUDawM1dTmntBYfmT2Y6J+nwyFTagIIIAAAghUVSDY+xvi9r2wqntkcfG8NzLCIsy/asfBy/Vro+3w4BYEEEBgUwLm3NTGMQtOQSg7AjSwslMriSf0/Gls+uUMhUyoCCCAAAIIVF0gPGqK6C4HVn2frG2Q60aG6ySls5IPkXI8kL7hucz7a6NZ+x4kXgQQSL+AC9wF4egFv0t/pETYLEADKyPnwK7pfVisdo9P/qiWkZAJEwEEEEAAgeoLMP+qReO8NzLcTsdIMPzy6p+vjO3gX74veW30oxmLmnARQACB+gk4pyuCzsFgPXXe/PpFwc6tFaCB1VqpOl5nU4Z0K5cXTxeznnUMg60RQAABBBBInQDzr1ouSd4bGcy/arnu8bSfiX/+stR9nxIQAgggkGYBNX0gPHvhEaqSPC/CV5oFaGCluTrrY2sa3/cakejsDIRKiAgggAACCNRUgPlXxWxkMP+q5bpHfzpDbMm0mn4PshkCCCCQB4HAyZeCMS/8Ig+55DkHGlgpr65N6XlquUlvTHmYhIcAAggggEBdBJh/VcBGBvOvWiy6NS2T6Pr9RSyuy/cimyKAAAJZFnDq1gbaaYiOeW5mlvPIe+w0sFJcYZs0YNfIN80ws+1SHCahIYAAAgggUB8B5l+13MhYuzRpZOyX/JrVpy5V3pX5Vy0D5/210SofK5ZHAAEEJFB50g049mA94PIyHOkUoIGVzrok465E4/G9bvUqx6c0RMJCAAEEEECgrgJuh8MlOPb3dY0hjZvnvZER7PMf4gZ9LI30dY0pnvbzZP7VpXWNgc0RQACBrAsEoVwcjHrhO1nPI6/x08BKaWWjSX0v8HF0ZUrDIywEEEAAAQTqLhDs/XVx+36y7nGkLYC8D/IOh/9BdKd908Ze93iYf1X3EhAAAgjkQMCpRj7QDzeMWvB4DtLJXQo0sFJY0uZXB71vejY2657C8AgJAQQQQACBVAgw/6rlMuS6kdE8/2rkdJGgIRVnMC1BMP8qLZUgDgQQyIOAOpkV9tpliH744dV5yCdPOdDASmE1107oM1XNn5zC0AgJAQQQQACBdAgw/6rFOuS9kcH8q5a//fL+2mg6ftMhCgQQKJJAEAY/DUbN/0qRcs5CrjSwUlYlm9T3nHIcXZ2ysAgHAQQQQACBVAkw/6qYjYxgn28l868uSNVZTEMwzL9KQxWIAQEE8iTgRLyVGg8vnTX7wTzllfVcaGClqIL2p147R0t0ZvKpgz1SFBahIIAAAgggkDoB5l+1XJK8NzLC4Tcl868Gp+481jug6E9nii15ot5hsD8CCCCQKwFVeT7c9cj9dNi4NblKLMPJ0MBKUfGiCXvc4C0+LUUhEQoCCCCAAAKpFGD+Vctlyff8q8b1868aU3km6xWUNS2X6Pr9RCyuVwjsiwACCORWQFW/Wxq78D9zm2DGEqOBlZKCrZ28xyiN4kkpCYcwEEAAAQQQSK8A869arE3+518Nl2D4Fek9l3WKzL98v8T3nVun3dkWAQQQyLdA86cSBo3+ID3jxafynWk2sqOBlYI62dQh20fLFje/OrhjCsIhBAQQQAABBFIt4LY/TILjGBe5cZHyPsib+Vctf1vG034h/vlfpvp7luAQQACBLAsEqk+7AcccpAdcXs5yHnmInQZWCqrYNL73tclz3yNTEAohIIAAAgggkHqBYOBF4gZ/KvVx1jpA5l/VWjwd+zH/Kh11IAoEEMi3gLrSRaUxc3+U7yzTnx0NrDrXKJrU+xQf2x/rHAbbI4AAAgggkBmB8KhrRXc5KDPx1ipQ5l/VSjo9+zD/Kj21IBIEEMi3gFO3Ngi67Kejnp2V70zTnR0NrDrWx+44aLvorbeeNS871zEMtkYAAQQQQCA7Asy/arFWeW9kuJ2OTuZfXZmdc1qjSP0rD0h87zk12o1tEEAAgWILqNOHw9ELD00+ndAXW6J+2dPAqp+9NI3vmwzwiPhTRx1rwNYIIIAAAtkSYP5Vy/XK+yDvYJ9vihv08Wwd1hpEGz/5S/GzflGDndgCAQQQQKBZoKThZ3TsvN+iUR8BGlj1cReb3PfYchT9uU7bsy0CCCCAAAKZFGD+Vctly/sg73D4jaI77ZfJM1vNoKNbR4q9+3g1t2BtBBBAAIENBJzIymAbG6SnvLgQmNoL0MCqvbnYQ0M7xwvffMab36MO27MlAggggAACmRVg/lXLpcv1IG/XKKWR00WCxsye22oEnvfXRqthxpoIIIBAJQScBreFY+efWIm1WKNtAjSw2uZVkavLk/p+z+Lo3yuyGIsggAACCCBQFAHmX7VY6bw3MtyOR0lwzFVFOeWtzpP5V62m4kIEEECg4gKlIDxXR8+7puILs+BmBWhg1fiA2JTeA+KyTk+evuKvEWtsz3YIIIAAAtkWcNsfIsFx47OdRBWiz//8q39P5l99ogpy2V6S+VfZrh/RI4BAtgVU9Z1wmx576ohpi7KdSbaip4FVw3qZicaTet7lvR5Zw23ZCgEEEEAAgVwIBAO/Jm7wp3ORSyWTyP38q6NvEN15/0qS5WKt+NazxL/7WC5yIQkEEEAgiwJO9cpw7EL+hqWGxaOBVUPsaPzun/DiLq/hlmyFAAIIIIBAbgSYf9VyKXM9yLt5/tWZT4uEnXJzjiuRSN5fG62EEWsggAAC1RZwTsx0q6NKo5+9p9p7sf57AjSwanQSbOqQ7aOli2eZ2PY12pJtEEAAAQQQyI8A869arGXeGxnMv2r5W9i/8qDE956dn+9vMkEAAQQyKqBOZob9j91PD7i8nNEUMhU2DawalatpYu/x4m1sjbZjGwQQQAABBHIlwPyrTTUyHkgaGefkqtYbJhMM+oa4fS7MbX7tTcw/eanEs37e3tu5DwEEEECgggLqSheVxsz9UQWXZKlNCNDAqsHRKE/a+0i1FcnsK554qwE3WyCAQAYE9IOni+u+l9ibD4t/+wERvyYDURNiPQWCgV9N5l99pp4hpHLvvA/yDo++Ppl/NSSV9vUMKr5tlPjFj9YzBPZGAAEEEFgv4FRWBVvb3nrKiwtBqa4ADazq+orNPaExevz56eZtQJW3YnkEEEAgMwLB4ePEffCI9+L1sdiSBUkz63Gx1x8Q/+a9yX+3KjO5EGhtBJh/1bJzvudflZL5VzOYf7VR6a1phUTXDxaxuDbffOyCAAIIILBFAafBbeHY+Sdu8UIu6JAADawO8W355vKknt+1WL+15Su5AgEEECiKgEp4+jTRTt1bTpiGVlEOQuvz1DBpZEwXKXVp/T0FuDL/86+GSXDM7wpQybalaK8+KNE9zL9qmxpXI4AAAtUXcKWGU8Oz5vyh+jsVdwcaWFWsvU3pPSAu63RvvrGK27A0AgggkCkB3XawhCfd1PqYaWi13iqnVzL/ahO93pwP8mb+1SbqzvyrnP5OR1oIIJB5AedeLu3SeS8dNnNF5nNJaQI0sKpUGDPRaGLfu82i9e/IVGkjlkUAAQQyJuAGfFaCA77S/qg3bmi9dZ9IvLL963Fn6gWYf1XMRgbzr1quO/OvUv9bFgEigECBBYKg9JNg9NyvFpigqqnTwKoSb3RNv9FeyxOrtDzLIoAAApkVCA7/v2T+1ZGVi5+GVuUsU7pSOGyy6K4fSml09QsrvvUs8e8+Vr8AqrmzY/5VS7zWtHL9/KuomvqsjQACCCDQTgGnGvlSlwMbRs58up1LcNtmBGhgVeF42NQRXcpLn52VTNfcvQrLsyQCCCCQYYEtzL+qRGY0tCqhmJ41kvlX4RlPizZ0TU9MKYgk//OvjkzmX/1fCqTTFQLzr9JVD6JBAAEEWhJQ9Y+FY14aqioeocoK0MCqrOe61crje33fRL5ehaVZEgEEEMi0gHbbV8KTazzbkoZWps+M2+4QCY4fn+kcqhG8Z/5VNVhTv6Zn/lXqa0SACCCAQLOAC9wF4egFfBJJhY8DDawKg9of9+gbr7BnGdxeYViWQwCBXAi4/p+R4MA6jwWgoZWps8T8q5bLlfdGRnj0daI7H5Cps1qLYOPbRotf/EgttmIPBBBAAIEOCKjqW2GXngP01HuWdGAZbt1IgAZWhY9ENLHnn7zXEyu8LMshgAACuRAIDvuduN2HpSsXGlrpqsdG0TD/quXy5H7+VfLaqJS6pPps1jy48iopX7dvMqGC+Vc1t2dDBBBAoB0CgdrPgrEv/ls7buWWTQjQwKrg0Yiu7T3Cl+3mCi7JUggggECuBMLTpol27pHunDZoaPm3p4m9cU/ybvjidMec1+iYf9ViZa1pxfpB3nEuK+92OEKCY8flMreOJGWvPiTRPWM7sgT3IoAAAgjUUKB5oHsQRPvpqJefreG2ud6KBlaFymtzT2iMHp89w7zvX6ElWQYBBBDIlYB22yeZf/XH7OVEQ6tuNWP+Vcv0eR/kHez9DXH7Xli3c5fWjf2Tl0k862dpDY+4EEAAAQRaEHDq7gjHLjgGnMoI0MCqjKPEE/p9K7bydyu0HMsggAACuRNw/T+dzL/6WvbzoqFVsxoy/6pl6tzPvzpqiuguB9bsnGVlI+ZfZaVSxIkAAgj8o0ApbPyIjpqdwb/FTV8laWBVoCZ2Q9/d4tXR88lnZPIZ3xXwZAkEEMinQCrnX1WCmoZWJRRbXIP5Vy3TxreNSgZ5P1o197ou7EpSYv7VP5eA+Vd1PZZsjgACCHREwGm4INj10IE6bNyajqzDvSI0sCpwCqIJvad4szMrsBRLIIAAArkVyMT8q0ro09CqhGLyJ5RQwqSRoQ383dCGoNa0cv38q3wO8mb+VcvfPvbqw8n8qzGV+d5iFQQQQACBmgsEZt8Mznnxv2u+cc42pIHVwYKWp+xxlDXFd3ZwGW5HAAEEci2g3QYl868K+hkXNLTadbbddh+W4PgJ7bo3zzcx/yrP1d10bv6pX0n83E+LmTxZI4AAAjkQcCqrgtJ2/6Ijp72Ug3TqlgINrA7Qm0kYTezzpJkf1IFluBUBBBDIvYDr/6lk/tVFuc+zVQnS0GoVU7DXV8Tt99lWXVuki5h/VaRq/z3X+PYx4t95uJjJkzUCCCCQFwGViQ1jX+DjZDtQTxpYHcCzCXt8rmzxZR1YglsRQACBQggEh14lrudRhci1zUnS0GqRLBw2SXTXg9vMmfcbcj3Im/lXLR9f5l/l/dua/BBAoCACzokFYcPhOnLOAwVJueJp0sBqJ6nddOS20coX55rY9u1cgtsQQACBwggUZv5VJSpKQ4v5V5s6RzlvZLgdDpfg2N9X4rsoV2vYa49IdPfoXOVEMggggEBRBQLVp9yYhQeqSlxUg47kTQOrnXrxxD4/jL3PwefBtxOA2xBAAIFWCug2e0s4Ymorr+ayfxIoYEOL+Vctfx/Yqw8lg7zz++ZBsPfXxe37SX4T2EjAP/XrZP7VT3BBAAEEEMiJgBN/YXj2S1fkJJ2apkEDqx3cNmlwr9gvnZV88mCndtzOLQgggEChBFy/ZP7VQcy/qljRC9DQYv5Vy6fFP3mZxLN+VrGjlLaFwqOmiO5yYNrCqns88e1jk/lXD9U9DgJAAAEEEKiMgKq+FXZf2k9PXLysMisWZxUaWO2oddPEPpPE+1HtuJVbEEAAgcIJBIdemcy/Orpwedcs4Rw2tJh/1fLpYf5Vzb6r0rNRzl8bTQ80kSCAAAK1FVAJv1M6e97Ftd01+7vRwGpjDW1C/4NibXrEe8GujXZcjgACxRQIT3tCtPN2xUy+Hlk3N7QWzxZ7c5r4tx4Ve+t+kShDf8GnoYRnPC3a0LUeeqnd05pWSnT9YBGLUhtjRwJz2x8mwXFXd2SJXN5rrz2azL/i70xzWVySQgCBQgs41dWBcwN09PyXCw3RxuRpwrQRrHxN7/tN7dA23sblCCCAQCEFdJuByfyrWwqZe1qStkUzxb/4V5E3Hxa/5MnUN0DcdkMlOH5iWvhSE4e9+nAy/2pMauKpdCDBwK+JG/zpSi+b+fWYf5X5EpIAAgggsEmBpIl1VTh24cchar0ADazWW4lN7Hd62Zevb8MtXIoAAggUWsD1+2Qy/+rrhTZIVfLRarFFs8Tenp7853Hxb94r4lelKsRgr38Tt9/nUhVTGoLxT/0qGeT90zSEUpUYwqOuTeZfHVSVtbO8KPOvslw9YkcAAQQ2L+CSP4UFnewAPePFp7BqnQANrNY5iT1xYSmafcdMM9+vlbdwGQIIIFB4geCQK8T1Gl54h9QCbDw/q7mh1fROXcMNj5wo+oGhdY0hjZvHt49JBnk/nMbQOh6TK0kpeW1USl06vlaeVmD+VZ6qSS4IIIBAiwLO5PbwnBdOgKd1AjSwWuck8cRe/y/28vNWXs5lCCCAAAKJQHjqY6JddsAiKwLmRZa9LH7RDLHXHxZ7426xtW/ULnrmX7VsnfNGBvOvWi47869q91sPOyGAAAL1FCiF4XE6at5f6hlDVvamgdWKStktg7pHS1fMNTOmELfCi0sQQACBZgHd+l8kPOVWMDIuYCtfT143fCYZBv9U0tS6S2zFnKplxPyrTTQymH9VtTOX5oX907+ReOaP0xwisSGAAAIIVELA3IzS2Qv2U5XkbxL52pwADaxWnI94Qs+fxqZfbsWlXIIAAgggsF7A9b1Qgg99A4+cCaxraL2VNLTeThpabyefcrh0RjIYPq5IlsG/fFnc/p+vyFp5WoT5V3mqZutziW8/O3lt9MHW38CVCCCAAAKZFXBaOj8cO3dcZhOoUeA0sLYAbTf37B0vD2Z58401qgnbIIAAArkQCA65PJl/dUwuciGJzQgkr7fZO8+vGwzv3302ee3wHpHy4naRMf+qZTbmX7XrOGX7pubXRq8fnIz3LWc7D6JHAAEEEGiVgDr3ahhtNUDPnbGyVTcU9CIaWFsofNP4Xs2f5T26oOeDtBFAAIF2C4QfeVS0647tvp8bMyqQDIaX5a+8N0er+SmtNx9s3WuHGkh4xnTRhq4ZTbxKYed+/tUhEhw3vkp42V3WXn9MorvOym4CRI4AAggg0GaBwJW+FYyZ+70231igG2hgbabYNn7PQbGseTp5ETX5hEu+EEAAAQRaK6Bb75nMv7qttZdzXc4FbPVisUXNrx0mnzT31qPiFz/2T68duh4HS3DCpJxLtD09e+0Rie7O79+jBQO/Jm7wp9sOk/M7/NO/TeZf/SjnWZIeAggggMCGAs7piqCb9dOTXqjhJ+hkqwY0sDZTr7UT+9ys3o/IVkmJ9v+zdx9wWlRX48fPnZln6SBFUBRhlwVBFEVsaOwNayyIsAuWaDQxif+YRN+UN1HTTEw0b4qxpAsLiCUaY0GwRrEACioiCLuLhQ4CSttnZu7/LhijBNmnzDzPlN9+Pnxi3LnnnvO9j4JnZ84ggAAC5Rdg/lX5zyDSGbibRK+t3/bY4cpZ5i6tp8XueyHzr3ZwaP4rt4j3xi8jfZzFJOccd5eo3Q8pJkQi13pTzPyrVcy/SuThUhQCCCCwMwFL/baipuFKkHYsQAPrMz4Zuq7/IZ5qesH3BSP+6UEAAQTyFLCPuN3Mvzopz1VcnlqB5scOvS0imbapJfiswr1Ha80g7+nJdFGOZM6bw7lvf7rMv0rm552qEEAAgRwELGU12R28AerMxQ05XJ66S2jOfMaRuxOqnvB9/9jUfSIoGAEEEAhAgPlXASASAoGkz7/qauZfDWf+1fYfdOZf8Y8+AgggkG4B23b+bI9eeEm6FXZcPQ2sHbjouj4nZ7U8ygcGAQQQQCB/AdXezL/6PPOv8pdjBQKfFkj+/KurzfyrKzj27QSYf8VHAgEEEEi3gGWJZ1vt91OjXp+Xbon/rp4G1nYmWotyJ1Y+r319KB8WBBBAAIH8BazqL4p96HfzX8gKBBD4lEDi518dO0lUT/64tf3H3ptygZl/9S/+aUAAAQQQSLWAmlwxpoHX0W73GaCBtX0Da2LliKyn7071PysUjwACCBQhYB9u5l9VMv+qCEKWIrBVIOnzr5wRs0VVtOO0PylgXnCQvXt/ET+LCwIIIIBAigXMXVjartBD1YjFr6SY4b9Kp4H1CRJz95Xt1vV5VYvsw4cEAQQQQKAwAeesF0S161HYYlYhgMA2gcTPvzrczL+q47S3E2D+FR8JBBBAAIF/C1hiPeSMqT8dkf8I0MD6xKfBDG6/2Axu/zMfEAQQQACBwgRU+73N/CtGCBamxyoE/iOgl7wo7pOjEkti7/MtsYZ8JbH1FVqYP/s28eb+vNDlrEMAAQQQSJhApsI9Ro189+mElVVwOTSwPqLTc6+r8GaPn+drt6pgTRYigAACKRew+l4q9mHfS7kC5SNQvIA/+/emkfGL4gNFNIJz7EQz/+qwiGZXvrSYf1U+e3ZGAAEEoiigtHo2M7bhyCjmVo6caGB9pO7V9bvS09lfl+MQ2BMBBBBIioB9+G1m/tXJSSmHOhAom4D36BjxVz9Xtv1D3Vg5wvyrHQgz/yrUjx3BEUAAgbgKZJQMV7WNU+Kaf5B508AymvrOwe1cZ/1C7ctuQeISCwEEEEibAPOv0nbi1BuKQPP8q3sOSOwgb6sr86929LnRS2eI+8TIUD5SBEUAAQQQiK+AbalZ1uiGg5USM6473V80sMz5ZydW/4/23J+l+6NA9QgggEBxAqp9fzP/ih8OFafIagTMD9YSP//qm2b+1Vc56u0E/Dm3i/c6fxzlg4EAAggg8N8CGZU5V9W+dV/abVLfwNp695X9QYPWete0fxioHwEEEChGwOp7iZl/9b/FhGAtAggYgcTPvzpmgqg9hnHW2wl4j10o/spncEEAAQQQQOC/BEzj5g2ntnGwuQvLSzNP6htY2XGVV2ulb0zzh4DaEUAAgSAE7GG3ilU1PIhQxEAg1QLeFDP/ahXzr1L1IWD+VaqOm2IRQACBQgQsnalxxr41sZC1SVmT6gaWfvKi1u57T9dr0bsn5UCpAwEEECiXAPOvyiXPvokSSPz8q2FiD5+QqCMLohjmXwWhSAwEEEAg2QLKknnO6MZ9zV1YfrIr/ezqUt3A8iZWXeV5/s1pPXzqRgABBIISUO36iXPWY0GFIw4CqRXQG1eJP+cW0SueF/3h/MQ52AO/IdaBX0tcXcUW5M+5w8y/uqHYMKxHAAEEEEi4gKXUSKe24e6El/mZ5aW2gbX17qulz5g3D/p7pPXwqRsBBBAISsDq+wUz/+r7QYUjDgIIGAG9eY3ImgWmmTVb9MoZ5rHC6WZA1uZY2zjMv9rh+XmPXWTmXz0d67MleQQQQACB8AXMXVhzzV1YzbOwUnkXVmobWF5dvys9nf11+B8xdkAAAQSSL2AP+72Zf3VK8gulQgTKKeBtEf3+QtGr3zQNrVmilz0hesvycmaU397KFmfEbFEV7fNbl/SrmX+V9BOmPgQQQCBQgUyFPkeNXPz3QIPGJFgqG1j6rVNauS/OX6jF3zMm50SaCCCAQKQFnLOeF9Vut0jnSHIIJFFAb1hqmlmvmbu0XjH/+6LotbNNmTqSpVpdDhP7lFTPnt3huTD/KpIfV5JCAAEEIitgKzXbqmk40NyFFc3f8EOUS2cDa1LVFVnXvyVEV0IjgAACqRFQbavEOfvx1NRLoQhEWUBvft88djjf3KU1b9tjh8vNY2n+xkikzPyrHR8D868i8fEkCQQQQCBWAlZGnemc3/BgrJIOINnUNbD0zMsy3luPLfA96ROAHyEQQACB1AtYfS82869+kHoHABCIpID5A49eW2+aWa9+9Njhk+axw2VlSdU5pk7UHoeXZe8ob8r8qyifDrkhgAAC0RSwlbxs1TQelLa7sNLXwJq012VZ17o9mh9DskIAAQTiJ8D8q/idGRmnWcA8bbD+HTMQ/nXz2KGZo7V8unnb4ZvhgzD/asfG7mbJ3j3Y3CWXDf8M2AEBBBBAIFEClqNPdUYtfiRRRbVQTKoaWFvvvnrzsflmXH9lmg6ZWhFAAIEwBZyzppv5V7uHuQWxEUAgRIHmtx1unaO10szPWvGi+GteMmO0vEB3tLocauZfTQo0ZhKC6WUzxX38vCSUQg0IIIAAAiUWUKJeyIxpGFbibcu6XaoaWO7E6kt8z/1jWcXZHAEEEEiQAPOvEnSYlILAvwWyG7e96XDZS9vmaK16ztwhtKUoH3vgVWIdeGVRMZK42H/1DvFeuyGJpVETAggggEAJBDIVbU5SI+dNLcFWkdgiNQ0srcX2JlTN97XfNxLyJIEAAggkQMCqMvOvhjH/KgFHSQkIfLaAecxNr11kmllzRC99VvwVz4h4G/ISc44Zb+ZfHZHXmjRc7E292Hg+lYZSqREBBBBAIAQBpdWzmbENR4YQOpIhU9PA2jKxz/nKE+5dj+THkKQQQCCuAvawW8SqOjWu6ZM3AggUIrD9YPil00Q3rfzsSFvnX70sqqJjIbsld423RbKT9y/67rbkAlEZAggggEAuAkrZx2VqFz2Zy7VxvyY1DSyvrs8sT8uBcT8w8kcAAQSiJJD5/HSR9sy/itKZkAsCJRfQZrro1sHwzW86fMXcpfWU6I0NH6fB/Ksdn4heNsvMvxpR8uNiQwQQQACBZAlYoh5zxjScnKyqdlxNKhpYelL1SVnXnZKGA6VGBBBAoFQCqm2lOGc/Uart2AcBBOIksP5t8ZsfOVxh7rzqWCXWoLFxyr4kuTL/qiTMbIIAAgikQiDTps1Qde68l5NebCoaWO74qmm++Mcn/TCpDwEEECilgFV1kZl/dW0pt2QvBBBAIDEC3tQvmPlXqXjiIzFnRiEIIIBAhAUmVoxprIlwfoGklvgGVtPkQQdY7oaXfV8SX2sgnwiCIIAAAjkK2MN+Z+ZfnZbj1VyGAAIIIPCxAPOv+DAggAACCAQoYFni2W2cvdXZCxcFGDZyoRLf1GkaX3mXiB4ZOXkSQgABBGIu4Hz+OVHte8a8CtJHAAEESi/A/KvSm7MjAgggkHQBS6nfObVAiE8OAAAgAElEQVQNX0tynYluYOl7+1d5m7Pzfa2dJB8itSGAAAKlFlBteotzzlOl3pb9EEAAgUQIMP8qEcdIEQgggECkBCwlG+3OTh916sKdvBo4UinnnUyiG1juxL1u9T3rS3mrsAABBBBAYKcCVtWFZv7VdSghgAACCBQgwPyrAtBYggACCCDQooBSzvWZ2oWJ/UN6YhtYekrf7t4qv9HcfdWmxVPmAgQQQACBvATsw34rVt/T81rDxQgggAACRmDr/KsDRPzNcCCAAAIIIBCogFJqjdOzbW917NwPAw0ckWCJbWBlJ/T+sfbV9yLiTBoIIIBAogQyZz4r0mGPRNVEMQgggEApBPSyl8V9/NxSbMUeCCCAAAIpFMhY9pWqZtFvk1h6IhtY+s7B7Vz7g8Va665JPDRqQgABBMopoNr0MvOvnilnCuyNAAIIxFbAf+0P4r3609jmT+IIIIAAAtEWsGxptEc19lNK3Ghnmn92iWxgZSf1/4Z2m27Kn4MVCCCAAAItCViVF4h9+PUtXcb3EUAAAQR2IOBNvUT8FU9ggwACCCCAQGgClmRqnTFvTQhtgzIFTlwDS8+8LJN9c+pCEb1XmUzZFgEEEEi0gH3Yb8z8qzMSXSPFIYAAAqEIMP8qFFaCIoAAAghsJ6CtVzNj6g8wd2HpJNkkroHlTupd67tqfJIOiVoQQACBKAnYB98kqvfxolp1ilJa5IIAAghEXkAvf0XcaedEPk8SRAABBBCIv0BGyXBV2zgl/pX8p4LENbCydXu9qLV1SJIOiVoQQACBKAqo9gNE9RgmatchYnUbLNKp+cbXxP22EkV6ckIAgZgK+K/90cy/+klMsydtBBBAAIE4CViWmuLUNAyPU84t5Zqo/9LIjut7uFbecy0VzfcRQAABBIIXUG32FOl2iFi7DjVNrf1FdekvYmWC34iICCCAQEwFvGmXir/88ZhmT9oIIIAAAnESsCzRttV+kBr1+rw45b2zXBPVwGqqq5wkWp+flMOhDgQQQCDWAnYHsboebJpZ5lf3A0xza19RFe1jXRLJI4AAAgULeE2Snby/iL+54BAsRAABBBBAIB8BS1u3OmPrr8hnTZSvTUwDS0/cu6fnZxt97fPj/ih/4sgNAQTSK6AcUZ32Mw2tQ01Da8i2u7Ta9UivB5UjgECqBJh/larjplgEEEAgEgKWko12j+57qRNeWh2JhIpMIjENrOyE6p9q3/1OkR4sRwABBBAooYBq09s0sw43zazmxw4Hi+rc14zRskqYAVshgAACpRFg/lVpnNkFAQQQQODTAkqrazJjG36RBJdENLD0W6e0cl96822tdfckHAo1IIAAAmkVUK3Mv8a7f06s7odsfexQ7VJt5mjZaeWgbgQQSJCAN+2LZv7VtARVRCkIIIAAAvEQUG9nahv6KiVuPPL97CwT0cByJ1Zf4nvuH+N+GOSPAAIIILCdgNPRDIU/YtscreZHDrvvx2B4PiQIIBA/Ad/Mv7priJl/tTF+uZMxAggggEDsBTJWZoSqeeveuBeSiAZW0/jK2SLaTMXkCwEEEEAg0QKfHAy/W/NdWvuahlZFokumOAQQiL+AXj5b3Glnx78QKkAAAQQQiKWAEvWvzJiGo2KZ/CeSjn0DK1vX91itvSfifhDkjwACCCBQgIDdTqxuZoZWj8NMM8vM0eo2UMSmoVWAJEsQQCBEAb1lnej6h8Svv0/02lkh7kRoBBBAAAEEPkPAsQ6pGFU/I84+sW9guXV9/u5rOSvOh0DuCCCAAAIBCVitTEPLPHK4m2lq7W6aWl0GMEMrIFrCIIBAQAJr68VveNj8ukf0psUBBSUMAggggAACLQgo/86K2rcvjLNTrBtY+t4hvb0t7y/yfWHCb5w/heSOAAIIhCWQ6SLWbsebZpZpaPUcJqpdj7B2Ii4CCCCQn4D2RS9/RfxGc2fW4vtE3HX5redqBBBAAAEE8hCwlNVk7+L3Vqc1LstjWaQujXUDy6vrfZOn1TciJUoyCCCAAAIRFVBi9TDNrH6jTUPrIFEVHSOaJ2khgEDqBNzN4r/7rLkr60HRSx82o11j/6Ko1B0hBSOAAAJxEFDKuT5Tu/C6OOS6oxxj28DSdw5u51ofvKtF7xJXfPJGAAEEECiTgHLM2w0/Z+7KOm7bnVmdq8uUCNsigAACnxbQm9eIXvy4mZl1n/hrXoAHAQQQQACBwASUUsudQwb0Vv0e2RJY0BIGim0Dyx2/1xd9se4ooRVbIYAAAggkVEDtcoBYfc8X1eckUa27JLRKykIAgdgJrFtsHjGcIv6iiWZeVmPs0idhBBBAAIHoCViOHuOMWlwXvcxazii2DSxvQuVMz9dDWy6RKxBAAAEEEMhRwMqYO7JOF3vAWPNmwyE5LuIyBBBAIGSB5nlZS18Sb+G9ot97QMTPhrwh4RFAAAEEkiqgRP0rM6bhqDjWF8sGVtPEvQ8Sb0usX/8Yxw8LOSOAAAJpErA6HyJqvyvE6tX8+3ssf7tM03FRKwKpEdj2iOGT4i80d2WtnZWauikUAQQQQCA4gYzj7adGvfN6cBFLEymWfyJ36yr/4Gt9aWmI2AUBBBBAIM0CzY0s66Dviuq+f5oZqB0BBCInYCbBrnjVNLLMrKzFk81dWZsjlyEJIYAAAghEVMBSv62oabgyotl9Zlqxa2DpPx3RwWuz5D3f1x3ihk2+CCCAAALxFVC9zhH7oG+LartrfIsgcwQQSKSAbtoguuFR8d/8g+gP5yeyRopCAAEEEAhOwLKsdbbbfg91wasbgosafqT4NbAmVV2Rdf1bwqdhBwQQQAABBLYTyHQxTawfi1V1CjQIIIBA9AT+PStrwSTRS/4por3o5UhGCCCAAAKRELBs6xJndP2fI5FMjknEroHl1VW+7GnNZN0cD5jLEEAAAQSCF7CqLhL74GtEnDbBByciAgggEICA3rBM9Ft/N48Y/lX0lhUBRCQEAggggECSBJTyX8rUvn1onGqKVQNLT6o+LOu6z8cJmFwRQAABBJIpoDrtJ87Rt4p02COZBVIVAggkQ8DbIn7DFPN44Z9Fr5uTjJqoAgEEEEAgGAHlH1RR+3Zs3ggSqwaWN7HqL57nXxTMSREFAQQQQACB4gRUq+5iH/NXUd0GFheI1QgggEAJBPSquaaRVSf+23ebxwvdEuzIFggggAACURawlH2HU7vo8ijn+MncYtPA0n8/ZhdvY+N7vpa2ccElTwQQQACBFAhkOps7sf4iqgdvKUzBaVMiAskQWP+OePMnib9onIj3QTJqogoEEEAAgbwFLEt9aO+ybg916pr1eS8uw4LYNLC8un5Xejr76zIYsSUCCCCAAAI7F7A7iHPCXdyJxecEAQRiJaC3rBe94F7x599u5mQtj1XuJIsAAgggEIxAxrG+okbV/z6YaOFGiU0DKzu+72tavH3D5SA6AggggAAChQmo1j3FOWkyM7EK42MVAgiUU8DdLP7iqeK/YRpZ6+eWMxP2RgABBBAotYC2Xq0YWx+LRwli0cDS4yqPzCr9TKnPkf0QQAABBBDIR0B12l+cU+4SsVvls4xrEUAAgWgI+J6Zj/Wk+HNNI2vtzGjkRBYIIIAAAqELKG0fkRm7aHroGxW5QSwaWE3jq80D+u6YImtlOQIIIIAAAqELWP2/LPbB14S+DxsggAACYQroZS+LP+dm8Vc9F+Y2xEYAAQQQiIKA8u80byO8MAqp7CyHyDew9OShnbzs6iUMb4/6R4n8EEAAAQT+LeAcUydqj8MBQQABBGIvoJe+JP5rt4i/kochYn+YFIAAAgh8hoAlssHusr5n1Ie5R7+BNa7v5Vnl3cYnDQEEEEAAgbgIqDZ9xDnzERGndVxSJk8EEEBgpwJ61VxzR9bvxF/2KFIIIIAAAgkUsGznUmf0wj9FubTIN7CyEyqna18PizIiuSGAAAIIILC9gL3/tWLtexEwCCCAQKIE9PLZppH1K+7IStSpUgwCCCAgopR6LlPb8LkoW0S6gaXvOaCf17R2vu9LpPOM8gGTGwIIIIBAmQScjuJ8/ilRrTuXKQG2RQABBMIT0O89L97smxn2Hh4xkRFAAIGSC2RadxioRrz2Zsk3znHDSDeGshOrf6I997s51sJlCCCAAAIIRErAHvy/Yu13SaRyIhkEEEAgOAEt/jvPiD/7F6LXzw0uLJEQQAABBMoioCznhkzNwsj2YCLbwNJarOzEqkbx/V5lOTk2RQABBBBAoEgB1bqnOGc9KWJXFBmJ5QgggECEBXxP/IaHzaOFN4netDjCiZIaAggggMDOBJRlveeMru+tlHhRlIpuA2vywBOzTZseiyIaOSGAAAIIIJCrgH3E7WL1OSnXy7kOAQQQiK+Au1n8eRPFe+P/RNz18a2DzBFAAIEUC2SUDFe1jVOiSBDZBlbThMrx4uvaKKKREwIIIIAAArkKqD3PEufoX+V6OdchgAACsRfQm9aI/9ofxF/4RxHtxr4eCkAAAQRSJaDUXRW1DaOiWHMkG1j64S4dvfc7LvW1tI0iGjkhgAACCCCQs4CVEefcWaIqOuS8hAsRQACBRAisf1vcV34l+t37E1EORSCAAAJpELCU1WT36NZTnfDS6qjVG8kGljup+lLfdf8QNSzyQQABBBBAoBAB+6i/itXr6EKWsgYBBBCIvYBe8oJ4M68X/UFkX2wVe2MKQAABBIIUyDjWV9So+t8HGTOIWJFsYGXHVf5LK/25IAokBgIIIIAAAuUWsPb+qtgHfbPcabA/AgggUD4B3xX/rQfEm/0jMx9rXfnyYGcEEEAAgRYFlKVmZmoaDm7xwhJfELkGlv5H70rvQ7XI9yVyuZX4bNgOAQQQQCAhAmqXoeKcdk9CqqEMBBBAoHABvWGZeLPM2wrf4d+JhSuyEgEEEAhfINO6/f5qxOuvhr9T7jtErkmUrav8odb6+7mXwJUIIIAAAghEXEA5khn1hoiZh8UXAggggICZ7b70JfFmXGceK5wHBwIIIIBABAVspW+2axdH6hGCSDWwtBbl1fVZ5ItURvD8SAkBBBBAAIGCBTKnPynSqU/B61mIAAIIJE7AbxJ/bp14r/9cxN+SuPIoCAEEEIizgFJqhVPTsIdSEpnXyUargTV5z6OzTc5TcT5kckcAAQQQQGBHAvZRfzGD3I8BBwEEEEBgOwG9dpH4L3xf/NXPY4MAAgggECGBjLJOVLX106KSUqQaWO6EvW7zfevyqOCQBwIIIIAAAkEJ2AfdKNbe5wUVjjgIIIBAsgR8zwx5v58h78k6VapBAIGYC9i282d79MJLolJGZBpYeuZlGffNqUu06G5RwSEPBBBAAAEEghKwB/2PWAd8KahwxEEAAQSSKfDhUvFevE78ZY8lsz6qQgABBGIkYIlab1fttps6/PlNUUg7Mg0st27gqb7e9FAUUMgBAQQQQACBoAWs6svEPvQ7QYclHgIIIJBAAS3+/HvEe+V6EW9DAuujJAQQQCA+AhmVOVfVvnVfFDKOTAOraXz1nSLu2CigkAMCCCCAAAJBC1hVF4o97LqgwxIPAQQQSK7A+rfFm/4dMxtrenJrpDIEEEAg4gKWre9xRi+OxByMSDSw9JMXtfaWPrPM9/1OET870kMAAQQQQKAgAav3KLE/d0NBa1mEAAIIpFageTbW/LvMbCzzAwA/m1oGCkcAAQTKJWAptdnOdNlNjZy1rlw5/HvfaDSwJlaOyHr67nJjsD8CCCCAAAJhCVh7mQbWkTSwwvIlLgIIJFtAr35TvOeuEv3Bm8kulOoQQACBCApkMhUXqfMX/K3cqUWigeVOqL7H991zy43B/ggggAACCIQlYFVeIPbhZp4LXwgggAAChQlkN4o342fiN4wrbD2rEEAAAQQKErAsNcWpaRhe0OIAF5W9gaX/dEQHr/WS5b7WbQKsi1AIIIAAAghESsCq/qIZ4v7dSOVEMggggEAcBfyF/zCNLPNSDH9jHNMnZwQQQCB2ApYlni1qD1XTsLycyZe/gVXX94Ks9sp+K1o5D4G9EUAAAQSSL2Af9Eux9uZm4+SfNBUigEBJBNYtFvdZ80jh2ldKsh2bIIAAAmkXyFj2lapm0W/L6VD2BpZb1/dhX3unlBOBvRFAAAEEEAhbIHPa4yK7VIW9DfERQACB9Ag0P1L40g3iN45PT81UigACCJRJQFnq+UxNw+Fl2n7rtmVtYOkHh3bz1r+/xNd+ppwI7I0AAggggECYAqpVd3HOfd78rmuFuQ2xEUAAgVQK+IseNI2sb/KWwlSePkUjgECpBMxjhNpur/uqMxc3lGrP7fcpbwNrXN/Ls8q7rVzFsy8CCCCAAAKlELB6jRT7qJ+XYiv2QAABBFIpoJfNFO9fV4huWpnK+ikaAQQQKIWAbcl37ZrGsr1Wu6wNrGxd9VNau0eXApo9EEAAAQQQKJeAfdAvzPyrEeXann1LIKC3rBV/xo2idj1QVLf9RHWuFrHsEuzMFggg8G8B/eES8Z7+KnOx+EgggAACIQnYSl62axuHhhS+xbBla2DpiXv39Lwt7/jmj3ctZskFCCCAAAIIxFiA+VcxPrwcU/ffeVq8Zy76z9V2O7G6DTMNrYNMQ2uwSLdBolp1zDEalyGAQMECZi6W+/y1ot+5p+AQLEQAAQQQ+GyBTEddVa7HCMvXwJpUdUXW9W/hg4EAAggggECSBZh/leTT/U9t3sybxJ//u50Wq9r0FtX9cNPUGmp+DTZ3afVlLlo6Ph5UWXIBLf7s28Wby6PbJadnQwQQSLyAkopvZcYsuKkchZatgeXWVU01w9tPKEfR7IkAAggggECpBJh/VSrp8u7jPnRO/o8tZTqbu7QOM82sg0V1HWju0jKPHla0K28h7I5AggT8+feKN+t/RLSXoKooBQEEECivQDnfRliWBpb++zG7eBvfXsHbB8v7wWN3BBBAAIHwBeyDbjTzr84LfyN2KJuA3rJO3HuGmP11cTlYGbF2MWMlupuGVrf9za99RbXrUVxMViOQcgH/nWfEe/bL5g2FG1MuQfkIIIBAMAJb30ao7N5q9KJ3gomYe5TyNLDq+l6Q1d7fck+TKxFAAAEEEIingHPaNFG7mEfF+EqswH/NvwqwUtWml7kz62Cxtj52aJpaXfqZ6aEVAe5AKASSL6BXzTPD3S8VvXlJ8oulQgQQQKAEAhllf03VLtr57IQQ8ihLA8ut6/N3X8tZIdRDSAQQQAABBCIjwPyryBxFqIl4s24W/83fhrrHx8G3Doc3jx12M8Phux/AY4elUWeXJAisWyzu42NFbyr5DQNJ0KMGBBBA4FMClqWfcmoWH1tqlpI3sPSDZ7T11r22wrx9kCEPpT5t9kMAAQQQKKmA1es8sY+6saR7slnpBdyHRpj5V7NKv/FHO6r2A0Ttfoz5ZRpbpqmlWnUqWy5sjECUBfSG5eJNu0j0h29GOU1yQwABBCIvYJkHs+3Osoc6rXFZKZMtfQOrrt85WZ29t5RFshcCCCCAAALlEGD+VTnUS7tnYPOvAkz744ZW9yHbHjtkjlaAuoSKu4DesEK8x00T64N5cS+F/BFAAIGyCmQc/3I16u07SplEyRtYTeOrx4m4Y0pZJHshgAACCCBQDgHn1KmiOleXY2v2LJHA1gHRz1xYot0K20Z1NMPgux8hardDzC8zS4s7tAqDZFViBPSm1aaJ9QXR615NTE0UggACCJRawBL1mDOm4eRS7lvSBpaeeVnGnz91uad151IWyV4IIIAAAgiUWkBV7CrOiBdElLnJmq/ECpR0/lUQisoWq7N5y+HuR5tfh5o7tPY1Q+EzQUQmBgKxEtBb1prHCc1g9zI+/hsrMJJFAAEEthOwlJW1u7bZTQ2fu6ZUOKVtYE0eeGK2adNjpSqOfRBAAAEEECiXgOo1QpyjflGu7dm3RALlnn9VdJnNQ+G7H2WaWZ8T1cM0tro03zFY0j8eFl0CARAoVGBrE2vqhdyJVSgg6xBAIPUCmUzFRer8BX8rFURJ/4Tijqv6va/8L5eqOPZBAAEEEECgXALMvyqXfOn21VvWi3uPeROg6NJtGvJOqk0vUT1PMI8aDjNNLdPQarVLyDsSHoHyCmwd7P5YjeiN9eVNhN0RQACBGApoy3qwVU39maVKvWQNLK3FcidWvqN93bNUxbEPAggggAAC5RJg/lW55Eu3bxzmXxWl0fy4YRczN2uPE8Xa6ziRTr2LCsdiBKIqoD9cYppYo0RveieqKZIXAgggEEkBS6nNds+2u6pj535YigRL1sDKTh4wTDdtnl6KotgDAQQQQACBsgpUdJXMiJeYf1XWQwh/c2/Wr8R/8zfhbxSRHba+3bDXcNPMMndodR3I5zsi50IaAQmsaxR36vmit6wIKCBhEEAAgXQIWJmKs53zF9xfimpL1sDyJvS90fO9q0tRFHsggAACCCBQTgHV61wz/+qX5UyBvUsgEPv5V0UYbX3UsNdpYu15rJmdNYRB8EVYsjQ6AnrVG6aJdZ6IvzE6SZEJAgggEHEBcxfWn5zahktLkWbJGljZuj7zzGOEA0pRFHsggAACCCBQTgF76M/FGjCynCmwd8gCSZx/VTBZprNYPYeL2vM4sfY4XCTTtuBQLESg3AL+20+K969LTBrJmW1XblP2RwCBZAuYF24vc0Y39lQq/H9xlqSBpf/RuzK7XjEZMdmfW6pDAAEEEPhIwDn1MVGd++GRYIHEz78q9Oys1mZm1qnmMcOTzd1ZR4o4bQqNxDoEyibgv36neHOuLdv+bIwAAgjETsCxDqkYVT8j7LxL08Ca0PdrWd9Lz5CIsE+N+AgggAAC0RXIdJHMeeb3b/PjKL6SK5C2+VcFnaTdzrzR0DSyKs80d2YdYR4zdAoKwyIEyiHgvfRz8d+6rRxbsycCCCAQOwElzg8zYxaG3vkvSQPLHdfnEV/J8NidAgkjgAACCCCQpwDzr/IEi+nl7kPniV47M6bZlz5tVbGrqN7nilV1uqhug0qfADsikK+A74n35FfFX/Zoviu5HgEEEEidgK3kZbu2cWjYhYfewNLTh7XxGpat9rXmHvKwT5P4CCCAAAJlF2D+VdmPIPQEdNMH4t5jBpdrL/S9kriB2mWIaWSNEFV1qqhWuySxRGpKiEDzP+veIyNEf7ggIRVRBgIIIBCOgGWJtls5e6lzF74bzg7boobewHInDTjddzc/GGYRxEYAAQQQQCAqAs4pZv5VF+ZfReU8wsjDf/dZ8Z4eG0bodMW0Wpk5WWeJ1c80s3Zr/qFt6H8sTZcv1QYioFe/Ke5jZ5k3E24JJB5BEEAAgaQKZBz/cjXq7TvCrC/0Pym446tu8cW/IswiiI0AAggggEAkBJh/FYljCDsJb9b/if/mr8PeJlXxVYcBppE11tyVdZq5K6tTqmqn2OgL+AvuE2/GN6OfKBkigAACZRTQlvVgq5r6M8NMIfwG1sQ+DeYR8j5hFkFsBBBAAAEEoiCg9jxbnKNvjkIq5BCiAPOvQsS12pqh7+eLVW3uyuq2T4gbERqB/AS85/5X/Ma6/BZxNQIIIJAiAUupTXbHfbupMx7cGFbZoTaw9IR9BmX9ja+HlTxxEUAAAQQQiJKAPfRnYg04P0opkUvAArppvZl/dSDzrwJ23VE4q9uRova5xDxmeCRv9SyBN1u0IJDdKO6U0aLXvQoVAggggMBnCFjaOt0ZW/9QWEChNrCy4yqv1krfGFbyxEUAAQQQQCBKAsy/itJphJML86/Ccd1ZVNW+v1j9LxTV9/OiKtqVPgF2ROAjAb12kbiPnMY8LD4RCCCAwGc3sG41DazQRkiF2sByJ1Q94fv+sZwuAggggAACiRdg/lXij7i5QO/lX4s/7/9SUWvkiqzoKna/S0WZuxxV686RS4+E0iHgv36neHOuTUexVIkAAgjkK2BZ72RG1/dWSnS+S3O5PrQGln64S0fv/V1W+drP5JII1yCAAAIIIBBnAeZfxfn0cs/dfXik6Pdn5L6AK4MXsFqL1fcisfYzzaw2XYOPT0QEdiZghvt6Uy8Wf9W/cEIAAQQQ2IFARloPVmPefC0MnPAaWBMrR2Q9fXcYSRMTAQQQQACBqAnYQ28w869GRS0t8glQQDd9YOZfDWH+VYCmRYWyWplG1sU0sopCZHFBAuvfkezDp5hbMjcUtJxFCCCAQJIFlFR8KzNmwU1h1BhaA8ubWP0nz3O/EEbSxEQAAQQQQCBqAs4pU0R16R+1tMgnQAH/3efEe3pMgBEJFYhA85sL+5th74MuMo8WdgkkJEEQaEnAn3+3eDOvaekyvo8AAgikTsCy1BSnpmF4GIWH0sDSWpRbV/meFr17GEkTEwEEEEAAgUgJMP8qUscRVjLMvwpLNqC4TiexB319252QTuuAghIGgc8Q0L54j13Eo4R8QBBAAIHtBCylNtk9j+6ijv3r5qBxwmlg3bPv4OzmD+cEnSzxEEAAAQQQiKKA2vMscY7+VRRTI6cABZh/FSBmiKFU655i7fsNsfqdJWLZIe5E6LQL6PffMm8lPNU8VuymnYL6EUAAgU8JKLvihMzoBY8HzRJKA8ubWHWV5/k3B50s8RBAAAEEEIiigH3gT8UaODqKqZFTQALMvwoIsoRh1C5DxD7w26J2P6SEu7JV2gS8GTeKv+DWtJVNvQgggMBOBWxt/dweW//toJlCaWC546v+6Yt/WtDJEg8BBBBAAIEoCjinPGrmX+0dxdTIKSAB5l8FBFmGMKrXOWIPvUZUux5l2J0tky6gmz4U78GTRW9ekvRSqQ8BBBDIWcBW6hW7tuHAnBfkeGHgDSwz/8rx6ipX+6I75pgDlyGAAAIIIBBfgUxnyYyYwaNK8T3BnDL3X/6NePN4TDQnrCheZHcw87GuMoPex5p/Vp0oZkhOMRbw6x8S7/mvxrgCUkcAAQSCFbAs0XYn6alOa1wWZOTAG1jZcX0P18p7LsgkiX++gYEAACAASURBVIUAAggggEBUBdQenxfnmP+LanrkFZCA9/D54r//UkDRCFMuAbXLQWIfep2oboPKlQL7JlGgeaD7lDHir34+idVREwIIIFCQgCWZWmfMWxMKWvwZiwJvYHkT+33f87I/DDJJYiGAAAIIIBBVAeZfRfVkgsur+REh954DzKBmL7igRCqjgDJvKvyq2AeYO2bsijLmwdZJEtBLZ4j7xMgklUQtCCCAQFECtm391R5df3FRQbZbHHgDK1tX/ZTW7tFBJkksBBBAAAEEoirA/Kuonkxween3nhP3qTHBBSRSJARU+wFiH/FL7saKxGkkIwlv2mXiL5+ajGKoAgEEEChSQFlqiTO6YU+lRBcZ6uPlgTaw9INntPXWz13ja79VUAkSBwEEEEAAgcgKMP8qskcTZGLMvwpSM2KxrIzYA78h1uAvMscuYkcTx3T0qrniTjk9jqmTMwIIIBCKQMZqu6+qeWNuUMGDbWDV9Tk5q+XRoJIjDgIIIIAAAlEWYP5VlE8nuNy8R0aJv+bF4AISKXICVrcjxTriZ6La94xcbiQULwH36atEv3t/vJImWwQQQCAkAfMY4TfMY4SBvQUn0AaWN6HvjZ7vXR1S7YRFAAEEEEAgUgL2gT8Ra2BNpHIimWAFmH8VrGeko2W6iHPE70TtMSzSaZJctAX02kXiPnSiSTKwJ2aiXTDZIYAAAjsRsJT9iFO76NSgkAJuYFXO9Hw9NKjkiIMAAggggECUBZzhj4rquneUUyS3IgWYf1UkYOyWK7EHXSPW/peJKCt22ZNwNATcp74u+r0HopEMWSCAAAJlFDC/k26wB5zUWR10RzaINAJrYOlHB3XxVm1Y6ZsJAkEkRgwEEEAAAQQiLeB0ksx5s5ibE+lDKj455l8VbxjHCM2PB9uH/1hURfs4pk/OZRbQy14W9/Fzy5wF2yOAAALREMg4zjA1auELQWQTXANrQr9zs372niCSIgYCCCCAAAJRF2D+VdRPKJj8vEdGm/lXgfyZK5iEiFIyAdVhgDhH3yrSqU/J9mSjpAho8xjhSNFrZyalIOpAAAEEChZQtvPtzOiFPy84wCcWBtbAcsdV/d5X/peDSIoYCCCAAAIIRF2A+VdRP6EA8stulOzd+5tRNm4AwQgRRwFVsavYx/5ZVLd945g+OZdRwK9/VLzn+U+jMh4BWyOAQEQEgpyDFVgDKzuh8k3tawaBRORDQhoIIIAAAuEKOMMfMfOvBoS7CdHLKqDfmy7uU7VlzYHNIyBgtRb7c3eI1evICCRDCrER8D1xHzhR9MaG2KRMoggggEAYApao9XZtQ1elpOifCAbSwNIP9dkt+74sDaNYYiKAAAIIIBA5gUxnyYyYwfyryB1MsAn5L/9WvHk3BxuUaPEUsDJiH/obsaqGxzN/si6LgP/6X8Wbc31Z9mZTBBBAIFICdquDK0bPL/q56kAaWG5d5Xm+1pMjBUQyCCCAAAIIhCSgep4pzrG/Dik6YaMiwPyrqJxEVPIwbygc+lOxBoyKSkLkEXEBvWGFuQvrcPMYshfxTEkPAQQQCFdAScW3MmMW3FTsLoE0sJomVv5aPH1lscmwHgEEEEAAgTgIMP8qDqdUZI7MvyoSMLnL7aE/M02s85NbIJUFKuA9/mXxlz0aaEyCIYAAAnET0Jb6R6uahs8Xm3cgDSyvrvJlT+shxSbDegQQQAABBOIg4Jz8sBnqPDAOqZJjgQLMvyoQLhXLzJ1Yw35rHic8LRXVUmRxAn7jY+I9d3lxQViNAAIIxFzAVup9q6ahm5mD5RdTStENLP1wl47e2o5rfF/sYhJhLQIIIIAAArEQcDpJ5rxZzL+KxWEVniTzrwq3S8VK5Yh95B/MYPdjUlEuRRYh4DVJ9t5hItk1RQRhKQIIIBB/gYxTcYAatWBOMZUU38Cq63NyVgv3xRZzCqxFAAEEEIiNAPOvYnNURSXqPVoj/urni4rB4oQLWK3EOeZOUbsfkvBCKa9YAe/FG8RfeEexYViPAAIIxFrAVpn/Z9e+9Ztiiii6gZWd0PvH2lffKyYJ1iKAAAIIIBAXAXvIj8XapzYu6ZJnIQLMvypELZ1rmu/IHH6/SKc+6ayfqnMS0Mtnizvt7Jyu5SIEEEAgqQKWsu9zahedW0x9xTew6qqf0to9upgkWIsAAggggEBcBJh/FZeTKjxP/d7z4j5VU3gAVqZKQHUcJPbJd4mqaJequik2DwHfE9c8RqibVuaxiEsRQACBZAkoUauc2obuZg6WLrSyohpYeu51Fd7sv77va2lbaAKsQwABBBBAIDYCzL+KzVEVk6j/yu/Ee6PoNz0XkwJrYyagep0jzpG/EFFWzDIn3VIJeNN/IH7DuFJtxz4IIIBAJAUyFf4gNfLtNwpNrqgGVnbygGG6afP0QjdnHQIIIIAAAnESUD1PF+fY38YpZXItQID5VwWgsUTs/a8Ta98LkUBghwL+4ifEe/YSdBBAAIFUC2Qk82U15q3bCkUoroE1od812s/+vNDNWYcAAggggECcBOwhPzLzr8bEKWVyzVeA+Vf5inH9vwWUbRrcE81Q94MxQeC/BHTTBvMY4RARP4sOAgggkF4BS/2toqbhokIBimpgbZlQ9Q/l+2cUujnrEEAAAQQQiJOAc/JDorrtE6eUyTVPAb3kBXGfHJ3nKi5HYJuAatNb7NPNvyeYh8VHYgcC3uNfEn/ZFGwQQACB1Aooy1qQqanfu1CAghtYWotyJ1Su1Fp3LXRz1iGAAAIIIBAbAaejZM57WcSyY5MyieYv4L9yi5l/9cv8F7ICgY8ErP5XiH3w1Xgg8F8C/ryJ4r38XWQQQACB1ApYlmi7Q9fu6oxZqwpBKLyBNanXvlnXfq2QTVmDAAIIIIBA3ARUz9PM40G/i1va5JungPdorfirGe+ZJxuXf1Kg+VHCE/8uatf9cEHgUwJ61RviTjkNFQQQQCDVApZqc5pTO+/hQhAKb2CN7/elrGRvLWRT1iCAAAIIIBA3AfuAH4o1aGzc0ibffASYf5WPFtfuREB13Fec0+41d2xW4ITAfwR8T7KTzRws7wNUEEAAgdQKKKV+lKlt+EEhAAU3sJrq9vqbaOuCQjZlDQIIIIAAAnETYP5V3E4s/3z1khfN/KtR+S9kBQI7ELAHf0+s/S7FBoFPCXjTLhN/+VRUEEAAgdQKWMqa5tTWn1gIQMENrGxdn3lmDtaAQjZlDQIIIIAAArESsDtIZmTz/CsnVmmTbH4CzL/Kz4urWxCw24lz5lOi2naDCoGPBfzX/iDeqz9FBAEEEEitgGWpD+zRDZ2VEi9fhIIaWHry0E5e0+o1vvmjfL4bcj0CCCCAAAJxE2D+VdxOrLB8vUfHmPlXzxW2mFUI7EDA6ne52Id8GxsEPhbQS2eI+8RIRBBAAIFUC2Qcbz816p3X80UoqIGVndj/eO01Tct3M65HAAEEEEAgjgLMv4rjqeWZc/P8q3sOEPGzeS7kcgR2ImBlzF1YT4tqtztMCGwTaP53zeR9zV9oRBBAAIHUCljiX+aMefsP+QIU1MDyJlV/23PdG/LdjOsRQAABBBCIo4Bz8j9FdRsUx9TJOUcB5l/lCMVleQtY/a8Q++Cr817HguQKuPefIHrDouQWSGUIIIBACwK27fzZHr3wknyhCmpguXV97/W1d06+m3E9AggggAACsRNg/lXsjqyQhP3Zvxdv7i8KWcoaBHYuYLUV5+x/iWrdBSkEtgq4T31d9HsPoIEAAgikVsA0ot7IjGnM+6fDBTWwsuOr3tHi75labQpHAAEEEEiNgLX7qWIfd0tq6k1rocy/SuvJl6Zue//rxdqXl3eXRjv6u9Awj/4ZkSECCIQrYFmi7Y4duqrTX3s/n53ybmDph/rsln1fluazCdcigAACCCAQVwHmX8X15PLIm/lXeWBxaSECqm2VOGdNFVG8/6gQv6St8RumiDf9S0kri3oQQACBvAQySoar2sYp+SzKu4HlTqw80/c097zmo8y1CCCAAAKxFXBOftDMv2oeuMtXUgX00pfMW8HOT2p51BURAee4u0TtfkhEsiGNcgroNW+J+8hJ5UyBvRFAAIGyC9iOXGuPavxhPonk3cDKTuj9Y+2r7+WzCdcigAACCCAQSwHmX8Xy2PJN2p99q5l/dWO+y7gegbwErKqLxR72g7zWcHFCBbwtkr3LjH7RXkILpCwEEECgZQEl6oHMmIazWr7yP1fk3cByx1dO8UXzI4N8lLkWAQQQQCCWAtbup5j5V7+PZe4knbuAN2WM+Kuey30BVyJQiECms2TOfUHErihkNWsSJuDed6ToTe8mrCrKQQABBPIQsKx3Kmrq98pjheTVwNJalDuhcqXWums+m3AtAggggAACcRRg8HIcTy3PnJl/lScYlxcj4BwzQdQew4oJwdqECHiP1oi/+vmEVEMZCCCAQGECmS5Od3XqwpW5rs6vgXXPAf2ym9cuyDU41yGAAAIIIBBnAeZfxfn0csud+Ve5OXFVMALW3l8V+6BvBhOMKLEWcJ/5luh37o11DSSPAAIIFCuQ7yD3vBpY7qTetb6rxhebJOsRQAABBBCIvMDW+VezRKxM5FMlwcIFmH9VuB0r8xdQ7fcW5/OP5r+QFYkT8Gb+Uvz5tySuLgpCAAEE8hGwLfmuXdN4Q65r8mpgNU3o+3/ie/8v1+BchwACCCCAQFwFmH8V15PLL2/mX+XnxdXFCzhnvSiqXffiAxEh1gL+3HHizWaof6wPkeQRQKBoAUupu53ahpG5BsqrgZUdX/mMFn1krsG5DgEEEEAAgbgKMP8qrieXR97uJsnevb+In81jEZciUJyAfdRfxOp1THFBWB17Ab/hMfGmXx77OigAAQQQKEbAUtYip7a+OtcYOTewtg5wr6tcYxpYu+QanOsQQAABBBCIq4Bz0j9E7bpfXNMn7xwE9NIZ4j6R8w/9cojIJQi0LGAPvEqsA69s+UKuSLSAXjZT3MfPS3SNFIcAAgi0JGBZou2OHbqq0197v6Vrm7+fewPrH70rs+tVfS5BuQYBBBBAAIFYC9jtzPyrV5h/FetDbDl5f/Zt4s39ecsXcgUCAQpYu50s9vG3BRiRUHEU0Kvmijvl9DimTs4IIIBAoAJK2cdlahc9mUvQnBtY7l39z/KzTX/PJSjXIIAAAgggEGcBa7fh5j8wb41zCeSeg4A35QLxV/0rhyu5BIHgBFSb3uKc81RwAYkUT4G19ZJ96Ph45k7WCCCAQIACSiq+lRmz4KZcQubcwPIm9rnW8+S6XIJyDQIIIIAAAnEWsPe/Tqx9L4xzCeTekgDzr1oS4vshCjjnvS6qol2IOxA68gIfLpXsA4dHPk0SRAABBEIXsFRdRU3DmFz2ybmB5U7oc5/vy9m5BOUaBBBAAAEE4izA/Ks4n15uuTP/KjcnrgpHwDltmqhd+oYTnKixENCb3xf33gNjkStJIoAAAmEKmKbUG5kxjYNy2SP3BlZd9SJfu1W5BOUaBBBAAAEEYitgtZXM+c3zrypiWwKJtyzgz7ldvNd/1vKFXIFACALOcXeJ2v2QECITMjYC7mbJ3jUwNumSKAIIIBCWgGXeB23v0a6TOnbuhy3tkVMDS//piA5eq/fW+uaP8y0F5PsIIIAAAgjEWYABy3E+vdxz9x67UPyVz+S+gCsRCFDAHnaLWFWnBhiRUPET0JKd0E9Ee/FLnYwRQACBgAWUto/IjF00vaWwOTWwsnftfYTObnm2pWB8HwEEEEAAgbgL2Ptfa+ZfXRT3Msh/ZwLMv+LzUWYB+8CfiDWwpsxZsH25BbITB5j7DraUOw32RwABBMoukNH2l9TYRbe3lEhODSw9qeqKrOvf0lIwvo8AAggggEDcBZyTHhC16+C4l0H+OxFg/hUfj3IL2IO/L9Z+Xyh3GuxfZoHshGruwCrzGbA9AghERMBSvzWD3K9sKZucGlhuXd/bfe1d1lIwvo8AAggggECsBZh/FevjyzV55l/lKsV1YQnY+3xLrCFfCSs8cWMhYB4hrGO8cCyOiiQRQCB0AUtZTzi19ce3tFFODazshMoXtK8PbSkY30cAAQQQQCDOAtZuJ4l9fIt3L8e5RHI3At5jF5n5V09jgUDZBKwBV4o99Kqy7c/GERDwPclONHdg8YUAAgggIEqpFZnahh4tUbTYwNJaLG9i5Trf1+1bCsb3EUAAAQQQiLOAPfgH5rGei+NcArm3JMD8q5aE+H4JBOyBV4l1YItPSpQgE7Yom4CfNQ2s/mXbno0RQACBqAlkujjd1akLV+4sr5YbWPft3z+7cd38qBVHPggggAACCAQt4Jx4v6ju+wcdlngREmD+VYQOI8Wp2AO/YRpYX0uxAKWLt0Wyk8wQd74QQAABBLYKKLv9sZnRrz9VVAPLras8z9d6MqYIIIAAAggkWoD5V4k+3n8X58+5Q7zXb0hFrRQZXQF70P+IdcCXopsgmYUvkN0o2cmDwt+HHRBAAIGYCGSU/TVVu+h3RTWwsnXV12ntXhuTmkkTAQQQQACBggSYf1UQW+wWMf8qdkeWyITtIT8Wa5/aRNZGUbkJ6M1rxL13aG4XcxUCCCCQAgHL9m9zRr/95aIaWE3jK+8y73cdmQIvSkQAAQQQSLEA869ScPjNj+xMNo+I+ltSUCwlRlnAPvTXYlWfGeUUyS1sgXWLJfvPY8LehfgIIIBAbASUqH9lxjQcVVwDa1zVHFH+4NhUTaIIIIAAAggUIMD8qwLQYrZEL5sp7uPnxSxr0k2igH3UX8TqRfMiiWeba0161RviTjkt18u5DgEEEEi8gK3U+3ZtQ5eCG1hb30A4ofJDMwOrTeK1KBABBBBAIL0CVmvJnD9HxKpIr0EKKmf+VQoOOSYlOif/U1Q35h/F5LhCSVO/97y4T9WEEpugCCCAQFwFMnarPdTo+Us+K/+dvoVQ39u/KrupaVFciydvBBBAAAEEchGwepwo9gl35HIp18RYwJt6sfgrnopxBaSeFAHn7Bmi2nZLSjnUUYCA3/CYeNMvL2AlSxBAAIHkCmQq2pykRs6bWlADy60beKqvNz2UXB4qQwABBBBAQIT5Vyn4FDD/KgWHHJMSrYxkRr1p3hduxSRh0gxDwJ9/t3gzrwkjNDERQACB2ArYtvUNe3T9rwpqYGUn9f+Gdptuim31JI4AAggggEAOAs4J94vqYYZ785VYAb1slpl/NSKx9VFYfARUh4HinPlwfBIm01AE/Nf+LN6rPwolNkERQACBuApYSv3JqW24tKAGllvX93Zfe5fFtXjyRgABBBBAoEUB5l+1SJSEC/xX7xDvtRuSUAo1xFzA6nWe2EfdGPMqSL9YAW/GjeIvuLXYMKxHAAEEEiWgbGt6ZnT9EQU1sLJ1lU9rrXf6GsNEaVEMAggggEDqBJh/lY4jZ/5VOs45DlXag78n1n6f+cPlOJRAjgEIuE/+P9FL/hFAJEIggAACyRFQSq3M1DZ0L7SBtdw0sD5zcXKYqAQBBBBAIK0C9uD/Nf8xeUlay09H3e5myd59gIi/JR31UmWkBewj/yzWXsdGOkeSC1/Afehs0Wtnh78ROyCAAAIxE8i069NZnf3U2h2l/ZlvIdSPDuqSXbVhdcxqJV0EEEAAAQTyEmD+VV5csbyY+VexPLbEJu2c9YKodj0SWx+F5SaQvcvMXXTX53YxVyGAAAJpErBbHVwxev7MvBpY2bv2PkJntzybJidqRQABBBBImUDz/KuRc8xrCCtSVni6ymX+VbrOO8rVqjZ7inPOv6KcIrmVQEBvWSfuPeauUL4QQAABBP5LwNKZGmfsWxPzamC5E6sv8T33j3gigAACCCCQVAGrxwlin/CHpJZHXR8JeFO/IP6KJ/FAoOwCVq+RZoD7z8ueBwmUV0CvWSDuIyeXNwl2RwABBCIqYDtyrT2q8Yd5NbC8CX1v9Hzv6ojWRFoIIIAAAggULcAw5aIJox/A2yLZyeZRHeZfRf+sUpChPfRnYg04PwWVUuLOBPx3nhHvmQtBQgABBBDYoYAzrmLMwgvyamBtmVD1D+X7ZyCKAAIIIIBAUgWcE/4uqgePcST1fJvr0steFvfxc5NcIrXFSCBzurkTsFOfGGVMqmEI+G/eJd6sb4cRmpgIIIBA7AWUqBcyYxqG5dXAytb1mae1DIh99RSAAAIIIIDAjgSsVh/Nv2qFT4IFmH+V4MONWWmqTS8z/+ppk/VnvkMpZhWRbqEC3os3iL/wjkKXsw4BBBBItIBSanWmtqFbzg0s07iyvAmVG3ytWydahuIQQAABBFIrYPU43sy/YtRj0j8A3tRLzPyrJ5JeJvXFQMDqe6nYh30vBpmSYtgC3tSLzb+Xngp7G+IjgAACsRXIdGvXVQ2fu2b7Anb4IyB9b/We2U3uO7GtlsQRQAABBBBoQcAe/L9i7XcJTkkW2Dr/yjwi6m9OcpXUFhMB59iJonoeFpNsSTNMAffuQ0Q3rQxzC2IjgAACsRbIqIpDVe2Cl3JrYN3V76hsNtt8jzNfCCCAAAIIJFKA+VeJPNZPFcX8q+SfcWwqzHSRzIjnRayK2KRMouEI6E1rxL1vaDjBiYoAAggkRMBy9Bhn1OK6nBpYbl2/i3yd/UtCaqcMBBBAAAEEPi3A/KtUfCL81/4g3qs/TUWtFBltAavqIrGHXRvtJMmuJAI01kvCzCYIIBBzAaWc6zO1C6/LqYGVHV99vRb3BzGvmfQRQAABBBDYoQDzr9LxwWD+VTrOOQ5VOsffLWq3g+KQKjmGLOC/Ocm8gfA7Ie9CeAQQQCDmAkomVNQ21ubUwGoaX32niDs25iWTPgIIIIAAAjsUsAd/z8y/uhSdJAt4TWb+1f7Mv0ryGcekNtW+vzhnPmJePmjFJGPSDFPAm36t+A3mP7X4QgABBBD4TAEl9ozMmEWH5NTAytZVPqu1PgJPBBBAAAEEkijgnHCfqB5DklgaNX0koJe/Iu60c/BAoOwC9v7Xi7XvBWXPgwSiIeD+8/Oi170ajWTIAgEEEIiogFJqRaa2oUduDazxlUu06N0jWgtpIYAAAgggULgA868Kt4vRSv+1P5r5Vz+JUcakmkgBq7U4Zz0nqk2XRJZHUXkKuJvNnaH7iWg3z4VcjgACCKRLwLJE2x32a6/OeHDjJytX2zPo6cPaeI1LN/i+/Nf30kVGtQgggAACSRSwuh8n9ol/SmJp1PQJAW/apeIvfxwTBMoqYFVfLvah3y5rDmweHQG96nVxp5wRnYTIBAEEEIiwQKZCDVAjG+bvvIE1ea99sk3W3AjXQWoIIIAAAggULGDv9x2xBl9W8HoWxkCA+VcxOKQ0pKgkc/qTIp16p6FYasxBwJ9/t3gzr8nhSi5BAAEEEMhUtDlJjZw3dacNLHfSgNN9d/ODcCGAAAIIIJBEAef4e83bwA5MYmnU9JEA86/4KERBwOpdI/bneIw1CmcRlRy8Z78j/uJJUUmHPBBAAIFIC1iO80Vn1MI/7rSB5dX1u9LT2V9HuhKSQwABBBBAoBCB5vlX580WcVoXspo1MRFg/lVMDirJaSrH3H1lHmHtuFeSq6S2PAXc+44Vvakxz1VcjgACCKRTwAxy/5EZ5P6DnTewxvf7lSfZr6eTiKoRQAABBJIsoDrtJ1bleSJ2K1Gd+pg3ER4gYlUkueRU1uZN+6KZfzUtlbVTdDQEmH0VjXOIUhZ6w3Jx7z8sSimRCwIIIBBtAeXfWVH79oU7bWBlx1feb95A+PloV0J2CCCAAAII5C/wyflXetMakXX15m6sNiLmDWGqTXfTzLLzD8qKaAn4TZK9a4iI/6mX1kQrR7JJtkCmizhnTOXNg8k+5byr8xuniffcF/NexwIEEEAgrQJKOU9nahces9MGlldX+YqntfmRNF8IIIAAAggkS8A5/m4z/+qgHRflezSwEnDcevkccaedlYBKKCGuAvbBN4nV/5y4pk/eIQl4M24Uf8GtIUUnLAIIIJA8AUukwRnTWLXTBla2rnKV1rpr8sqnIgQQQACBVAtYGTP/6lXmXyX8Q+C/9ifxXv1xwqukvKgKWF2HiX3yeBFl/tjNFwKfEHAfPF30el70zocCAQQQyFXAUlbWrqlvo5SYnzJv+1KfXKyfvKi1t/Spjb7/6b+f6wZchwACCCCAQFQFrO7HiH3iX6KaHnkFJMD8q4AgCZO/gNVaMqc+ImLm6/GFwKf+G2vDCjP/6lBQEEAAAQTyFMi0cXqpcxe+u+MG1r39q7KbmhblGZPLEUAAAQQQiLyAve93xNr/ssjnSYJFCGydf7W/mX+1uYggLEWgMAF76M/EGnB+YYtZlWgBv/4R8Z6/ItE1UhwCCCAQhoDS9hGZsYum77iBNa7yyKzSz4SxMTERQAABBBAop8BO51+VMzH2DkyA+VeBURIoTwG151niHHUTjw7m6ZaWy73p14rfcGdayqVOBBBAIDABS2dqnLFvTdxhA2vLpL6jlOt9/M3AdiUQAggggAAC5RTYOv9qzrY3DvKVWAH/tT+b+Vc/Smx9FBZNAdV+b7FPMS+IqOgQzQTJqrwC2hf3vqNEb36vvHmwOwIIIBBDAdtxvmOPWvizHTawspP6f0O7TebHR3whgAACCCCQHAFr16PFPumvySmISnYo4E27TPzlU9FBoHQCmc7inGSaV7v0Ld2e7BQrAb3qDXGnnBarnEkWAQQQiIqAJdbvnTH1X9lhA8ur632zp9VVUUmWPBBAAAEEEAhCgPlXQShGPMbW+VdDzPyrjRFPlPQSI6AccY7+m6g9Dk9MSRQSvIA/53bxXv/45oHgNyAiAgggkGABS9n3ObWLzt1hA6tpfOVdInpkguunNAQQQACBFAo4x00WtfvBKaw8PSXrFXPEnXpWegqm0jILKLGH3SJW1SllzoPtoy7gPjRC9NpZUU+T/BBAAIFICijbmp4ZXX/EDhtY2brKZ7XW0T0LBwAAIABJREFUH38zkhWQFAIIIIAAAvkIMP8qH63YXsv8q9geXQwTN82rQ38lVvXnY5g7KZdSQG9YKu793KFXSnP2QgCBZAlYyql3ahd+/Jy++mR57sQ+Db4nfZJVMtUggAACCKRZwNr1KDP/6m9pJkhF7f4rt4g3/1YRb0Mq6qXIMgkoW+yDfyFWv7PLlADbxknAnzdBvJe/F6eUyRUBBBCIlICl1CantqHtv5P6uIGltShvQtUmX/utIpUxySCAAAIIIFCEAPOvisCL21LzUzi9tl708hmilz5rBrpPMzOxsnGrgnyjKmDu5rQP/TWPDUb1fCKYl/fIaPHXvBDBzEgJAQQQiI9Apsv6TurUNeubM/5PA+vh6l2za9wV8SmDTBFAAAEEEGhZgPlXLRsl9orsRtErXhF/2Uuilz1jmluzE1sqhYUsUNFVnCNvE7XbQSFvRPikCOgPl4j7AJNZknKe1IEAAuUTyLTttLc6Z86CTzWwmiYPOkCaNrxSvrTYGQEEEEAAgYAFmudfjTBNi8zHdx4HvAHh4iSgN78veuWr5u6sF0QveVz0hrfilD65lklAdRxk3jb4e5GOe5UpA7aNo4A/d5x4s38Qx9TJGQEEEIiUQKbCPUaNfPfpTzWw3HFVp/nK/2ekMiUZBBBAAAEEihBg/lUReGlYuv5d85jhTHN3lmloLXtCdNPKNFRNjXkIWL1HmccGv08TPA8zLt0mwNsH+SQggAACwQhoW0a1Gt1416cbWJOqL/Vd9w/BbEEUBBBAAAEEyi9gD/ofsQ74UvkTIYPoC2hf9PsLzfysmeaRw+fNo4fPmP8C3Tpuga80CtgdzLD2n4rV9/Q0Vk/NRQroNfPFfWR4kVFYjgACCCDQLGAr+bpd2/jrTzWwvAl9vuP58lOIEEAAAQQQSIoA86+ScpJlqOO/BsKbO9f9jWVIhC1LLWB1P87cdXW9eWRwz1JvzX4JEfBm/EL8BeaxU74QQAABBIoWUJZzQ6Zm4Xc/3cCq632zp9VVRUcnAAIIIIAAAlEQYP5VFE4hOTl4TWZ+1uvmzqzmRw6fE3/187zhMDmnu7US1WZPc8fmt81bBk9t/n8Jq45ySibgN4l77+d4JLlk4GyEAAJJF7Bt66/26PqLP9XAahpffae5V35s0ounPgQQQACBdAhY3Y4U+2TzWxtfCIQh4G4SveoN09SabR47fFH8VS+IeB+EsRMxwxZwOoq9z9fEGlDDrKuwrVMQ31/8hHjPXpKCSikRAQQQKI2ApeVRZ2zjKZ9qYLnj+jziK+Fh7dKcAbsggAACCIQsYO9r7qTY//KQdyE8Ah8J/PuRw+a3HK6ctW0o/Jbl8ERZQNnmbqsLzL8nvmruvuoS5UzJLUYC3tRLxF/xRIwyJlUEEEAg2gK2UrPt2oYhn2pgZcf3fUmLd3C0Uyc7BBBAAAEEchNwjrtL1O6H5HYxVyEQtIAZCi/r3zb/IfuKaWiZXyvMmw43vBX0LsQrRMBuJ1ZlrdjNd1x16l1IBNYgsGOBtfWSfeh4dBBAAAEEAhRQlizL1DTu/qkGVlNdZaNoze/iAUITCgEEEECgTALMvyoTPNvuTEA3bRAxbyfTK+eI//7rIitNU2vzEtBKJKDa9BKr38Wi+p8rqlXHEu3KNmkS8F78mfgLb09TydSKAAIIhC5gKeXaNQ0VSon+eEKlW9dng6+lbei7swECCCCAAAIhC1jdjjDzr8aHvAvhESheQH+4RPTqeeaXaWitnm2Gw88ws7RMo4uvYARMM1vtdrJY1SPE2uNzIpYdTFyiILCdgG76UNz7DmcWHp8MBBBAIASBTJf1ndSpa9ZvbWDp6cPaZOuX8m7oEKAJiQACCCBQegF70DXmbWJfLv3G7IhAsQLNs7TWNYheY5pa7y8QWWvu2Fo7x8zTWlFs5PSsN00rq8eJovqcKmrPI0VVcLdVeg6/fJX6c8eJN/sH5UuAnRFAAIEEC2TsXSrV6NmN2xpYk4fulW1avTjB9VIaAggggECKBJh/laLDTkmpetMakXX1ppm10Pwys7Tef0P8ta+K+Pz8sfkjoFrtJqrHMaJ6Hm6aVkfziGBK/rmITJneFnHvP948EvxeZFIiEQQQQCBJApk2bYaqc+e9vLWB1VS311DR1swkFUgtCCCAAAIpFWD+VUoPPoVlNw+K/2CJ+KaxJaaptbW5td78+mB+4h9jUq26i+puGlU9DhVrt6EfDWP/eDJGCj8MlFxOAX/BfeLN+GY5U2BvBBBAINECGWWdqGrrp310B1bf4dkm75FEV0xxCCCAAAKpEGD+VSqOmSJbENh6x9aH75pmlrkj5IN3RH/4jsjG98z/N48nbn7X3H7vxsRQiWq/t6jOg82vASKd+5n/7Suq3W4mfxpWMTnEZKdpHvt1/3EKbxlN9ilTHQIIlFnADHIf6dQ23L31d353QvUY33fHlTkntkcAAQQQQKBoAXvQ1Wb+1RVFxyEAAokVaJ6ztWmVyKaVprG1TGSzaXY1//WWNaa5tVrU5hXm+8vMXy81jyhuDpfBbmdmVO0qqu0eIu16mcZUT/O/PU3TavdtTarmXxneMRTuIRC9GAF/0T/Fe+FrxYRgLQIIIIBACwIZbX9JjV10+9YGljex6irP829GDQEEEEAAgbgLMP8q7idI/pES8JtEZzeKym6S5resiflrcTeI9rPb/vqTd3I1f1/0tvTtNuaXs+2vM+3NzVKW+eWY2VQdtg5V1+aXamX+vlURqXJJBoG8BMw/B+4Dw0VvNI/x8oUAAgggEJqAbcl37ZrGG7Y2sLITq3+iPfe7oe1GYAQQQAABBEohwPyrUiizBwIIIICAEfDn3yPezKuxQAABBBAIWcC27F/YNYuu2fYI4cS9bvU960sh70l4BBBAAAEEQhWwuh4h9vDxoe5BcAQQQAABBMTdbGZfnWgetzUz5fhCAAEEEAhVwMzA+pOZgXXpR28h7FNn7viuCXVHgiOAAAIIIBCygD3oGjP/6ssh70J4BBBAAIG0C/iv/VG8V3+SdgbqRwABBEoiYFnyd6em8ZytDawtdVUPKu2fXpKd2QQBBBBAAIGQBJh/FRIsYRFAAAEEPhbQG1eZu6+OM4OEP0AFAQQQQKAEAko5T2dqFx6zbQZWXeXTWuujSrAvWyCAAAIIIBCOgBkQnTlvDm8sC0eXqAgggAACHwl4z31f/EYeV+cDgQACCJRKQCnrtUxt/eBtbyGsq3zF0/qAUm3OPggggAACCAQtwPyroEWJhwACCCCwvYBeNVfcKWeYv/3RGzchQgABBBAIXUBZ1nuZmvo9tw1xr6te5Gu3KvRd2QABBBBAAIGQBOxBV5v5V1eEFJ2wCCCAAAKpF/A9cR8dLfr9GamnAAABBBAopYClZKNT29hu2yOE4ytXatHdSpkAeyGAAAIIIBCkgHPsJFE9Dw0yJLEQQAABBBD4WMB/Y7x4r3wfEQQQQACBEguYIe7aHt2Y+egOrKotvvYrSpwD2yGAAAIIIBCMgJl/5YyYLaqiXTDxiIIAAggggMAnBPSG5eL+8yTz6Mp6XBBAAAEEyiCQ2bJHR6WfvKh19r2nNpVhf7ZEAAEEEEAgEAGr6+FiD68LJBZBEEAAAQQQ+LSAFu/xL4u/bAowCCCAAAJlEsh0lt2VntK3e3alt7xMObAtAggggAACRQvY+3xLrCFfKToOARBAAAEEENhewJ9/t3gzrwEGAQQQQKCMApkOdj+lH+hbnf3Ae6uMebA1AggggAACRQk4x040868OKyoGixFAAAEEEPgvgfXvSPbhU8xr2zeAgwACCCBQRoGMU3GA0vcOPDC7adOsMubB1ggggAACCBQuYOZfZc6bI5JpW3gMViKAAAIIILC9gHnroPfYBeKvno4NAggggECZBVSm1edUduK+x2jvwyfLnAvbI4AAAgggUJAA868KYmMRAggggEALAv7LvxFv3q9wQgABBBCIgEDGcU5W7sTKM31PPxCBfEgBAQQQQACBvAWYf5U3GQsQQAABBFoQ0EteEPfJGnOVxgoBBBBAIAICGZU5V7kTqsf4vjsuAvmQAgIIIIAAAnkLMP8qbzIWIIAAAgjsREBvWC7ew2eIblqJEwIIIIBARAQytnOB0uP6Xp5V3m0RyYk0EEAAAQQQyF3AzL9yRswWVdEu9zVciQACCCCAwGcJeE3iTb2YuVd8QhBAAIGICWSUc4XyJvT5uucLD3dH7HBIBwEEEECgZQGr6zCxh09o+UKuQAABBBBAoEUBLd5zPxC/cXyLV3IBAggggEBpBZRSVytvUvW3Pde9obRbsxsCCCCAAALFC9j7fFOsIV8tPhAREEAAAQRSL+C//jfx5lyXegcAEEAAgSgK2LZcp7J11ddp7V4bxQTJCQEEEEAAgZ0JOMdMELXHMJAQQAABBBAoSsB/+0nxnv2imdnuFRWHxQgggAAC4QjYlv0LlR3f5wbzbo1vh7MFURFAAAEEEAhJgPlXIcESFgEEEEiXgF4xR9zHR4n4m9NVONUigAACMRKwtHWr8up63+xpdVWM8iZVBBBAAAEEhPlXfAgQQAABBIoWWNco7mMjeeNg0ZAEQAABBEIWsNTflDuu6ve+8r8c8laERwABBBBAIFABe+A3xDrwa4HGJBgCCCCAQIoEPnjv/7N3H3BWVdfix9fe596hF1G6yMxQxNjFXhDUiD22pzBDiv8Uk5gYNb4YU19eTPLy8kx/5qUZjcyAFTvGLooFwd4QmAEB6aB05t6z939f0EQUnFvOvfeU33w+8yEJ+6y91nffUBbnrCPZBxrFblqQoKIpFQEEEIiogJJm9xbC+mt9Yy6IaAmkjQACCCCQUAHmXyX04CkbAQQQCEDAblgu/oOfFrv+zQCiEQIBBBBAoNwCWqmbVdvE2tz7x8eXezPiI4AAAgggEJiA8iR17guiaroGFpJACCCAAALJELAblon/gGtebZiTjIKpEgEEEIiBgFZyu8o2DbnVWP/sGNRDCQgggAACCRHQvQ4X7+RJCamWMhFAAAEEghKwG5a6O68ucHdevRFUSOIggAACCFRAQGt7r8o2D77HGHVKBfZjCwQQQAABBAIRYP5VIIwEQQABBJIl8O4C97bBzzLzKlmnTrUIIBATAa3VA+4OrPqHjDXHxaQmykAAAQQQSIBAanSTqIFHJqBSSkQAAQQQCELArnxd/Ec/J3bL8iDCEQMBBBBAoMIC7g6sR1VmUv106xv+FlBhfLZDAAEEEChSgPlXRcJxGQIIIJBMAbPoCfGf+KqIvy6ZAFSNAAIIxEBAKTVd+U21s3wrB8WgHkpAAAEEEEiAAPOvEnDIlIgAAggEJGBm3yr+rCtErB9QRMIggAACCFRDQCkzQ2Waa1+xRvauRgLsiQACCCCAQKEC3l6Xij7o4kIvYz0CCCCAQJIETFb8mf8jZs4fk1Q1tSKAAAKxFfCUet41sOpnW2OGx7ZKCkMAAQQQiJVAavREN//qqFjVRDEIIIAAAsEJ2E2rxTx+mZgVjwUXlEgIIIAAAlUVUFpezQ1xn+OGuA+taiZsjgACCCCAQD4CzL/KR4k1CCCAQGIF7MpXxJ/2VfemwYWJNaBwBBBAII4CSqvZuQbWXNfAGhLHAqkJAQQQQCBeArrXYeKdPDleRVENAggggEAAAlbM7NvEf+5KEZMJIB4hEEAAAQTCJKBFWl0Da+g8Y7P1YUqMXBBAAAEEENiRAPOv+FwggAACCHxYwG5aJWb6lWKWPQAOAggggEBMBZToRSo7qbbV+FIb0xopCwEEEEAgRgLMv4rRYVIKAgggEICAWfiYmGeuELtlWQDRCIEAAgggEFYBpdQyGlhhPR3yQgABBBDYXmDr/KvnRNV0RwYBBBBAIOECdss7Ymb8l5i3bky4BOUjgAACyRBQolaqtol1C0TsHskomSoRQAABBKIqoHc5VLxT+ItKVM+PvBFAAIFgBNysq5b7xDz3I+66CgaUKAgggEAkBNwdWMtVW3P9W2LMoEhkTJIIIIAAAokVYP5VYo+ewhFAAIFtAmsXif/sVWKW/gMRBBBAAIGECWx9hDDTXL/IGjMwYbVTLgIIIIBAxAS8YyeK3v2oiGVNuggggAACpQrYtnViXrnWvWXwf3nDYKmYXI8AAghEVEBpWeoaWHWLrbEDIloDaSOAAAIIJEGA+VdJOGVqRAABBLYXcG+aMq33innhv8RufhsdBBBAAIEECyit3laZiXVvW7H9E+xA6QgggAACIRdQuxwiqVNuCnmWpIcAAgggEIhArnE1/35319Vvxa57I5CQBEEAAQQQiLaA0nqxuwOrdok10i/apZA9AggggECcBZh/FefTpTYEEEDgPYFc4+qth8S87BpXa1+FBQEEEEAAgX8KKNGLVKapbpm1tg8uCCCAAAIIhFWA+VdhPRnyQgABBAIQMFnXuHpYzEu/4o6rADgJgQACCMRTQL2Va2Atdw2s3vEskKoQQAABBCIvwPyryB8hBSCAAAI7ErBt68W23CPmtT+I3bQAJAQQQAABBHYuoNSC3AysFW4G1m44IYAAAgggEEYB5l+F8VTICQEEEChewK6eI2bubW5Ae7NIdm3xgbgSAQQQQCAxAtqT+TxCmJjjplAEEEAgmgJ6r0vEO+gb0UyerBFAAAEEtglkNm6db2XfbBKz+hlUEEAAAQQQKEhAi7S6Ie71i6wxAwu6ksUIIIAAAghUSMA79gbRux9dod3YBgEEEEAgMAH3pii78lV3p9W97nuSu9vq3cBCEwgBBBBAIFkCWul5KjupttW98KM2WaVTLQIIIIBANASUpM59QVSH7tFIlyyLEjCzb3aPE00S1fswUX0OdD/uL6pL36JicRECCFRZINe0WvW6u9vqQbHzpzDbqsrHwfYIIIBAXARcA2uue4Sw/k1rzbC4FEUdCCCAAALxEWD+VXzO8uMqyT52mdhFU7ZbojoNds2sI10za6T73k/ULkNElLt5nC8EEAifwHZNq1td02ph+HIkIwQQQACBSAsopee4Ie61r1qRT0S6EpJHAAEEEIilgB7xDfFGXhLL2ijqPQH3F9/MLYeKtK36WBLVwd2R1eco0bu5O7R221fUriNEvA4wIoBAlQTs+rfFLp3pvp923w+L3bKsSpmwLQIIIIBAEgSUVrNV28S6F0Ts/kkomBoRQAABBKIl4I36u+hBx0QrabItSCD3NrLs1BMLumbrYpUS1cM1st577FDvsqdIj8GFx+EKBBDIS8BueVfs8hfELnENqyWPiF0/O6/rWIQAAggggEAQAu5G/FfdEPe6Z62xBwcRkBgIIIAAAggEJ8D8q+AswxvJvHGT+LOuCCTBrY8d9j7Cfefu0tpHVK/hIjoVSGyCIJBkAbtptfhP/0js23cmmYHaEUAAAQSqKOBpNSvXwHrSNbCOqGIebI0AAggggMBHBFTPgyV16s3IxFwgO+2bYhfeVp4qdVr0Lu7f6Pq44fC9D3Aztdx3hx7l2YuoCCRAwK58TczzV4tZ/nACqqVEBBBAAIEwCSilprsh7nWPWWtHhSkxckEAAQQQQID5Vwn4DOQ5/ypICdV1hKi+7i6tXfbeNhy+11AXXgW5BbEQiL2Afftpd+fkT8SufSX2tVIgAggggEA4BNxbCB9W2ab6B4w1J4QjJbJAAAEEEEBgmwDzr+L/SbBr3Pyre4uYfxUgjerQz92hdaTo99922MsNh+exwwCFCRVbAeOLmXuHmJd+7ga4L49tmRSGAAIIIBAOAa3tva6BNeReY/2Tw5ESWSCAAAIIIJATYP5VEj4H5o0b3V0c3w5XqV4X0bvmHjk8RFS/Q92P+7huak24ciQbBEIkYNvWi31toviv/1rEbAlRZqSCAAIIIBAnAa2829SW5ro7lLFnxKkwakEAAQQQiLaA6jnSzb+6JdpFkH27AmWdf9Xu7nkueG+Oluo3atsMLffYoaQ753kxyxBIkMDaRZJ187HsotsTVDSlIoAAAghUUGCSyjYPvcWY7DkV3JStEEAAAQQQ+FgBPeLr4o28DKU4C1Rh/lUgnLmG1m5HuWbW4e5729sOaWgFIkuQmAjYxdPFf9a9sXDDnJhURBkIIIAAAmEQ8LT+m2prrp8kxowLQ0LkgAACCCCAQE7AG3W96EG8XyTOn4YwzL8KxDfX0OrlHjXs7+7Q6ucePdxtb2ZoBQJLkEgLZDeLeeVv7rHCX7nHCjORLoXkEUAAAQTCIaA983+qrWmP68Xqz4QjJbJAAAEEEEAgN//qOVEdekIRYwHzxk1u/tUV8avQ6ya6z9GuoeW++7o5WrzlMH5nTEX5C6xbLP6Mq8QsvS//a1iJAAIIIIDAjgS09xv3COEe/2eMvhAhBBBAAAEEwiDA/KswnEL5c8hOu1zswlvLv1GVd1CdBosacLy7O+sI9z1SVMddqpwR2yNQaQErpuVe8Wf+QCSzutKbsx8CCCCAQEwElMh/KX/ykP/xs/43Y1ITZSCAAAIIRFxAj7jYzb+6NOJVkP7HCrj5V9lbDhfbtiJxUKrrCHd31mj3fbjofocwPytxn4DkFmw3rXazsX7mGte8oCO5nwIqRwABBIoXUCr1I5WZOPRHVrLun0T4QgABBBBAoPoCzL+q/hmUOwO7Zq5k7/1kubcJf3zdWXRf18waOMZ9Hymq64Dw50yGCJQoYFr/IWbm9xPZwC6RjssRQACBRAt4Wr6jMs3DvmVN5ueJlqB4BBBAAIGQCOTmXz3v5l/1CEk+pFEOATP7Zvc40bfKETrSMVWPfUUPPNE9cniMqN7u7Ybai3Q9JI/AzgS23o0148diF90OEgIIIIAAAnkJeJ6+TNnJ9V/NZM3/5nUFixBAAAEEECijAPOvyogbotDZaf/OY0TtnUe6l+gBJ4kadNzWxw1VTZf2ruDnEYiYgJuNNecO9zKH74n4GyKWO+kigAACCFRaIJ3SFyl74/DPZjJt11V6c/ZDAAEEEEDgwwJ6xNfd/KvLgImzQG7+1a1HiN2yPM5VBlubSonuPUrUHqe4b/e4YadewcYnGgLVFFi7UPzp/y5m9TPVzIK9EUAAAQRCLpBW3meVbR52TsZkmKYY8sMiPQQQQCAJAsy/iv8pM/+qxDNWnmtmuUcMB40VvfuxIl37lxiQyxEIgYDJiHnxT+K/drVLxoYgIVJAAAEEEAibQDrV4UxlbxpyUqbNnxq25MgHAQQQQCBpArn5V8+5+Vc9k1Z4oupl/lWwx616Hih6j9NF1Y1lCHywtESrgoB9+xnxn7yYOzSrYM+WCCCAQNgFlPKOcw2s4Udn2toeD3uy5IcAAgggEG8B5l/F+3zfr475V+U75382s+rd7Kwu3JlVPmkil1PAblwh5onLxayYVs5tiI0AAgggEDUBZQ5W9pbBB2Y2q+eiljv5IoAAAgjES0Dv+TXxDv5mvIqimu0FmH9VmU/E1scMjxVV6+7MGuyGwNd0r8y+7IJAUALukUL/ud+Imc17poIiJQ4CCCAQdYF0x57DXQPrgGGZze+8GfViyB8BBBBAINoC3qjrRA9yM334iq2AfWeeZO85Ibb1hbIw3UH0wDNE1X/KvdXwMBGdCmWaJIXAjgTM/AfEf+oSEbMRIAQQQACBhAuke3t9lZ2054CMv2Vxwi0oHwEEEECgqgJu/tU5s0R13KWqWbB5eQWYf1Ve33aj1+zqmsRniK49TVS/A91y1e4lLECg2gK5Fz/4j14odmNLtVNhfwQQQACBKgqkB47upOy9vbpnVnd/t4p5sDUCCCCAQMIFVM8DJHXqlIQrxL98f9q3xCy8Of6FRqBC1WNf0UPGuzuzTubFCRE4r6SnaDeuFPPY18WsfjrpFNSPAAIIJFJAK92WamzpoKwVL9NUm02kAkUjgAACCIRCQO95kZt/dXkociGJMgkw/6pMsCWGzT1iOOgc0UPP4a6sEim5vMwC/hb3OOGPxCyYVOaNCI8AAgggEDYBJWplekJr7633jmebajcYK53DliT5IIAAAggkQ4D5V/E/Z+Zfhf+MVec60XXniRrumlmde4c/YTJMoIAV88IfxX/15wmsnZIRQACB5ApolWpJNc4dsrWBlWmuX2SNGZhcDipHAAEEEKieAPOvqmdfuZ3N7FvEn/nvlduQnYoX0Gl3V9a/iR7RIGq3vYuPw5UIlEnAvHGj+LOudNFtmXYgLAIIIIBAmAQ8pZ73GlsP2tbAaqp/yVqzb5gSJBcEEEAAgWQIMP8qGefM/KtonrPudZi7I6vRDX4fK+LVRLMIso6lgJn/oHuk8GvuDYVbYlkfRSGAAAII/EtAqdRj6ca5o99rYNU9Zq0dBRACCCCAAAKVFtDDvyreIdyZU2n3iu7H/KuKcpdjM9VpkOhhF4gadiZvCy0HMDGLErBLZ0r2sc+7eShri7qeixBAAAEEoiFgtb6rQ0PLGe/PwJriZmCdGY3UyRIBBBBAIE4C3qi/uceVRsepJGr5sMA7LZK553hc4iCgO7o5WQ3i7fUZkR6D41ARNURcwK54SbIPu89jlpeqR/woSR8BBBDYuYBWTTUNrRO2NrD8SUP/6vvZ/4cXAggggAAClRVg/lVlvauzG/OvquNe3l3d+4AGnSXeJ77g5mTtVd6tiI5AOwJ25WuuifVpNxdlNVYIIIAAAjEU0Fb/IfXplq++18Aa9gvfz/D+8hgeNCUhgAACYRZg/lWYTye43PxpV4hZeFNwAYkUKgHd5zjRe39R1IDDQ5UXySRLwK6eI/7DE8RuWZ6swqkWAQQQSICA8uxV6fELvr+tgXXD4O/4Sv0kAXVTIgIIIIBAiASYfxWiwyhXKlvnXx3p/lK5rFw7EDckArr3KNfI+rKogblG1tY/YvKFQEUF7OrZkn2wgTuxKqrOZggggED5BTwll3iN83+z9U8XtmnoVzI2e035t2UHBBBAAAEE/iXA/KsEfBqYf5WAQ96+RL3rUaLQ16sdAAAgAElEQVT3+xp3ZCXu5MNR8NbHCR8az2D3cBwHWSCAAAKBCGhJN6YmzGne2sDaMqn2fOXL5EAiEwQBBBBAAIG8BHLzr2a6N5r1yms1i6IpwPyraJ5bEFnrPmNEH3CJqN77BRGOGAjkLWDfftq9ndANdjeZvK9hIQIIIIBAeAXSqdRYNW7u/dvuwLppr09m2jbdH950yQwBBBBAIG4Cqsf+kjrt9riVRT0fEmD+FR8J3f9k0Qd9U1TPIWAgUDEBs+Ah8adf6P6i41dsTzZCAAEEECiPQLqjPUidu+D5rQ2stqY9RorVM8uzFVERQAABBBD4qIAe/hXxDvkWNHEWYP5VnE+3sNqUJ7r+M6L3v0hUp10Lu5bVCBQpwB2gRcJxGQIIIBAygbTn7aHGz1u47Q6sW4fXZza1zQtZjqSDAAIIIBBjAe+Ya0XvMSbGFVKavNsqmbuPAwKBfwl43cTb+1I37L1RRNcgg0DZBfyZV4uZ/fuy78MGCCCAAALlE0jX9++sjnxq07YG1pTRPTMb5q8p33ZERgABBBBAYHuB1Nmz3J0YzL+K8+fCzL5V/JmXx7lEaitSQHWuF33gFaJrTywyApchkKdA7k7QaZeLXTQlzwtYhgACCCAQJgGt1fpUQ2u3XE7bGlhWlN9c12asTYUpUXJBAAEEEIinAPOv4nmuH67Kf/zbYt66MRnFUmVRArrfWPEO/rZIj9qiruciBPISyGyU7P2fEfvOrLyWswgBBBBAIDwC2pP5qfHz6/7ZwMr9h0xT3TJrbZ/wpEkmCCCAAAJxFWD+VVxP9oN1WcnecqTYLUuTUCw1liKg0+LtdYnofb8g4vFYYSmUXLtzAbthqfhTz3S/Ji2DCQEEEEAgQgJKq5nphtZDtmtgtd1Q/6Iow3uOI3SQpIoAAghEVYD5V1E9uQLyZv5VAVgs3fqH0q4jxDv8Z6L6HgAIAmURsEufk+zD57vHT7JliU9QBBBAAIHgBbTypqYa552yXQMrO7HuH0YsgwiC9yYiAggggMCHBJh/Ff+PBPOv4n/G5alQiR52oXgHfl0k3bk8WxA10QLm5T+L/9JPE21A8QgggECkBJT5e03jW5/droHV1rTH9WL1ZyJVCMkigAACCEROQPXYT1Kn3RG5vEm4MAHmXxXmxertBVSnweId9lNRA4+EBoFgBXJD3R+9WOzb9wQbl2gIIIAAAmUR8JT9pde44JvbNbD85vqf+8Z8qyw7EhQBBBBAAIH3BPTwL4t3yBV4xFqA+VexPt6KFefuxhr6RfFGXiqS6lixXdko/gJ2yzvi3+PmYW1aEP9iqRABBBCIuICn5Ttew/yfbd/AmlR/qe+bX0a8NtJHAAEEEAi5APOvQn5AQaT37nzJ3D0miEjEQEBUj30ldfSvRXrWo4FAYAJ26SzJPvRvLp4NLCaBEEAAAQSCF9BivpSa8Naft2tgZW8YNt6oTHPw2xERAQQQQACBfwkw/yr+nwbz5m3iP7v1Tm++EAhGQHcW76D/FL3nOcHEIwoCTsB/9r/FvPkHLBBAAAEEQiygrT4t9emWrc99q/fzzDQNGWOt/3CI8yY1BBBAAIGIC2y9k+K0OyNeBem3J+A/fqWYtya3t4yfR6BgAT3oPNGHf19UTdeCr+UCBD4ikN0s2annil37KjgIIIAAAmEVqOlyYM15r76wXQPLTt5nr0x2/WthzZm8EEAAAQSiL6CHuflXhzL/Kvon+XEVuPlXtx4tdvPb8S6T6qomoLqOEO+Y34jqNbxqObBxfATsylcle/+Z7knCbHyKohIEEEAgRgLpXqk+6pS5K7ZvYE0Z3TOzYf6aGNVJKQgggAACIRPwjv6r6MHHhSwr0glUgPlXgXISbCcCuqN4R/xOdO0JECFQsoA/61di3vhtyXEIgAACCCAQrIBWus1raOmo1LaBhf98hDD3X7JNdRuNtZ2C3ZJoCCCAAAIIbBNg/lX8PwnMv4r/GYenQiXeft8Xve/nPvxH2vCkSCbREMhslOxdp7q3Es6PRr5kiQACCCREQHsyPzV+ft375W7fwJpU22p8qU2IBWUigAACCFRQgPlXFcSu4lbMv6oifkK31rWN7m6sH4jomoQKUHYQAuatR8R//P8FEYoYCCCAAAIBCSilpqcbW4/eYQMrM7HuKSv28ID2IgwCCCCAAAL/FNDD3fyrQ5h/Fe+PRG7+1TFu/tXieJdJdaET0H1Gix71K1EdeoYuNxKKjkD20UvELr4jOgmTKQIIIBB3AaVurGlsHbfDBla2qXaKseKmGPKFAAIIIIBAsALe0X9x86+ODzYo0cIlwPyrcJ1HwrJR3dxw9+P/JqpLv4RVTrmBCaxbLJm7x4iYTGAhCYQAAgggULyAp+wvvcYF39xxA2vSHn8wvv5y8eG5EgEEEEAAgR0LpM6eKarTrvDEWMC8OUX8Zy+LcYWUFnYB1blevE/eIKrrgLCnSn4hFfBnXi1m9u9Dmh1pIYAAAskSUFJzeXrCm1fvsIHlNw/7rm8yVyWLhGoRQAABBMotoLrvI6nT7yr3NsSvsoD/xJViFkyuchZsn3QB1WmQpI6/QaTH4KRTUH8RAnbLu5K9w92FleHl7EXwcQkCCCAQqID1ZFyH8fNv3GEDy04a+umMn/17oDsSDAEEEEAg8QJ6mJt/dSjzr+L9QWD+VbzPN1rVqU67v9fEqo1W4mQbCgHz8rXiv/TjUORCEggggECSBdI1Nceo8958YscNrBuHjcpkMo8lGYjaEUAAAQSCF2D+VfCmoYvI/KvQHUnSE6KJlfRPQAn1ZzdJ9s6xYjctLCEIlyKAAAIIlCqQ7m7r1RkLWnfcwLr1wMGZTWvml7oJ1yOAAAIIIPBBAeZfxf/zYOa4+VczmH8V/5OOVoVbm1ifdI+1dhsYrcTJtuoC5o0bxZ/17arnQQIIIIBAUgW0Fuv1H91Zjblu844bWFZSfnPdJmNtKqlI1I0AAgggEKwA86+C9QxrNOZfhfVkyEt13VO8sc2iOvYCA4H8BbKbJXv7GLFbluZ/DSsRQAABBAITUKJWpie09v5gQPXh6G0T6xaI2D0C25VACCCAAAKJFtDDLnTzr/hX7Hh/CJh/Fe/zjX51utfh4p3wV5F05+gXQwUVEzAv/9XNwuL9VhUDZyMEEEBgOwH1Ys2E1gM+toGVuaHucavs0cghgAACCCAQhIB31J9F154QRChihFWA+VdhPRny+oCAGnCGpEb/SkRpXBDIS8C2rZPslGNEsu/mtZ5FCCCAAALBCbg7sO5wd2Cd+bENrLam2iax0hDctkRCAAEEEEiyAPOv4n/6Zs7tbv7VpfEvlAojL+Dtc6Xo/b8U+ToooHIC/nO/EfP6ryu3ITshgAACCGwV8CT9a2/CnO3+gPmRRwgzzUN/ak32SswQQAABBBAoVUB120tSZ9xbahiuD7mA/8R3xCyYFPIsSQ+BnIASb9S1ogeNhgOB/ATWL5HMne4uLOvnt55VCCCAAAKBCHgq/Q2vcc5vPxjsIw0sO3HYlzOS+UMgOxIEAQQQQCDRAsy/SsLxu/lXt41yr5tflIRiqTEOAqkekj75bpHuu8ehGmqogID/8EVilvCPMRWgZgsEEEDgnwI61fH01Lg33G/Y//r6SAMrO3nwySar+BWaDw4CCCCAQMkCzL8qmTD8AZh/Ff4zIsOPCKieB0vqpCb3fEINOgi0K2AWThN/2mfbXccCBBBAAIHgBNK68z6q4bVXP7aBZZs/sXfGbHwluG2JhAACCCCQVIHUWc+K6rxbUstPRN3Mv0rEMceySG+vS0UfdHEsa6OogAWML9nbx7g7TRcGHJhwCCCAAAI7E0ib7l3VZ17a8PENrEf27ppZvGEdjAgggAACCJQiwPyrUvSic63/xHfd/Kvm6CRMpgi8L6A8SZ14u6jd9sEEgXYFzEt/Ev/ln7W7jgUIIIAAAqULuBcGL003zO//4UgfeYQwtyDTVLfKWtur9G2JgAACCCCQVAE99ELxDvt2UstPSN3Mv0rIQce2TNV1T0mdOkUk1Sm2NVJYQAJrF0nmLjfMnS8EEEAAgbILKK2eSje0HplXA8tvqp3lWzmo7FmxAQIIIIBAbAW8o/4kuvaTsa2PwpzAuwskc/doKBCItIC3z7dF739hpGsg+coIZO89T+yaZyuzGbsggAACSRZQ0lzTOL8xrwZW28S6G927Ys9Lshe1I4AAAgiUJpA68xlRXfqUFoSrQy3A/KtQHw/J5SugO0j61AfcWwkH5XsF6xIqYF6bKP7z309o9ZSNAAIIVE5Aefaq9PgFH/kFd8ePEDYPvsoa9d3KpcdOCCCAAAJxElDdRkjqjKlxKoladiDA/Cs+FnERUANOldSY38elHOook4DdsMQNcz/KRbdl2oGwCCCAAAI5Ae3pz6fGt1z7YY0dNrDsjcM/m8m0XQcdAggggAACxQjooV9y86+uLOZSromMAPOvInNUJJqXQOq4m0T1PySvtSxKroB/3wQxq6YnF4DKEUAAgQoIKK/rmPT4Vx7Nq4GVuWHIkVb5/MpcgYNhCwQQQCCOAsy/iuOpfqgm5l8l4JCTVaLe9QjxTmpyRe/w33eThUG1OxUwr1wv/ov/gRACCCCAQBkF0p12qVXnPL8grwaWvXdo78zq7PIy5kNoBBBAAIEYC6TOmiGqc+8YV0hpZu6d4j/zDSAQiJWAN+p60YNGxaomiglWwK6eLdmpJwUblGgIIIAAAv8U0EpnvIaWTkqJn1cDK7coM7FujRXbE0cEEEAAAQQKEWD+VSFa0V3rT/+emPm5u1X4QiA+AqrnwZI69SZXEHdhxedUA67EGsnedozYzW8HHJhwCCCAAAI5AdfAmpdqbBm6I42d/u6cmThkhhWfQQB8hhBAAAEEChJg/lVBXBFdnJt/dazYTQsjmj9pI7BzgdQJt4nqeyBECOxUwH/yB2Jab0AIAQQQQKAMAlp5U1ON804pqIHV1lTb5F6w0VCGfAiJAAIIIBBjAe+oP4quPTHGFVKarF0ombt4zIpPQjwF1IAz3BsJfxPP4qgqEAHTer/4T14YSCyCIIAAAghsL+Ap+0uvccE3C2pgZZqG/oe12R+CiQACCCCAQCECqTOfEdWlTyGXsDZiAsy/itiBkW6BAkpSZ053v471L/A6lidFwG5cIdkphyalXOpEAAEEKiqQTpkL1bi3/lRQAys7eXCjyaqJFc2UzRBAAAEEIi3A/KtIH1/eyTP/Km8qFkZUwNvvO6L3/WJEsyftSghkpxwndmNrJbZiDwQQQCBRAul0+lh1/pxpBTWwbNPwQzO27ZlESVEsAggggEBJAnroF8U77DslxeDisAsw/yrsJ0R+pQuoTrWSOushN8tdlx6MCLEU8B+/Usxbk2NZG0UhgAAC1RRI9/b6qrHzlhfWwLp7310y76xbXc3E2RsBBBBAIFoC3pFu/lUd86+idWoFZsv8qwLBWB5VgdQJt7th7vtHNX3yLrOAeeNG8Wd9u8y7EB4BBBBIloBSanW6sXXXnVX9se8IzjTVrbTW7vTiZFFSLQIIIIBAewKpM592c2P6treMn4+wAPOvInx4pF6QgB5xsXgjLy3oGhYnR8CufFWy/zgtOQVTKQIIIFABAdfAmu4aWEcX18BqrnvSGntEBfJkCwQQQACBiAuorntK6lP3RbwK0m9PwJ/+fTHzGZHZnhM/H30B1blu22OE8rH/3hv9QqmgOAHTJpkb9xExmeKu5yoEEEAAgY8IeF7qWm/83M8X1cDym+uv9Y25AFcEEEAAAQTaE9BDviDe4d9tbxk/H3GB7G2jxG5aGPEqSB+B/ARSpz4oqueQ/BazKnEC2bvPEPvuy4mrm4IRQACBcgkoq76V/nTrL4pqYGUmDv+mlbb/KVdyxEUAAQQQiI8A86/ic5Y7rYT5Vwk4ZEr8oIA38ueiR5wHCgI7FPCnfUvMwpvRQQABBBAISECn1Rmp81vvKqqBZZtqx2as8DxIQIdBGAQQQCDOAsy/ivPpbquN+VfxP2Mq3F5A7X6WpI79JSwI7FDAvPQn8V/+GToIIIAAAgEJpLt5w9Sn5s0troF169DdM5uyPCcQ0GEQBgEEEIirAPOv4nqy29flP/kDMa03JKNYqkQgJ5DuJenzZrr/wBwsPhAfFTDzHxR/+hehQQABBBAIQEAr3eY1tHRRSrJFNbByF2Um1q2xYnsGkA8hEEAAAQRiKsD8q5ge7IfKyt422s2/WpCMYqkSgfcE0p96UqRrfzwQ+IiAXTNHsveeiAwCCCCAQAACSrxX0hPm7ftxodr956TMpPrp1jdHBpAPIRBAAAEEYirgHfl/ouvGxrQ6ytoqsHaRZO46BgwEEifgjbpe9KBRiaubgvMQyGyUzE1757GQJQgggAAC7Qloz96SGr/g30pqYGWbhvzRWP9L7W3GzyOAAAIIJFeA+VfxP3sz7y7xn744/oVSIQIfEvD2+47ofXlMjA/GjgUyN410j6yshgcBBBBAoEQBpe1P0g0LvldSA8tvqv2Gb+XXJebC5QgggAACMRVg/lVMD/ZDZTH/KhnnTJUfFdC1E8Q76sfQILBDgew9Z4l95wV0EEAAAQRKFLCejOswfv6NJTWwMpOGH2/9tgdLzIXLEUAAAQRiKuDtc6Xo/blRN6bH+8+ymH8V9xOmvp0J6N2OEm/sRIAQ2HED65GviX37HnQQQAABBEoUSHfstpc69+U3Smpg2Xtq+2XWyJISc+FyBBBAAIE4CuiOkjpzuqhOveJYHTW9L8D8Kz4LCRZQHQdI6pzpCRag9I8T8J/5qZi5fwYJAQQQQKAEAa3UJq+htZt7A6FfUgMrd7F7E+EK9ybC3UrIh0sRQAABBGIooId9WbxDr4hhZZT0QQHmX/F5SLpAevwcEZ1KOgP170DAvPxX8V+6ChsEEEAAgRIE3BsIn3VvIDy0vRDtvoVwawOrqe4xay2vX2lPk59HAIFIC6gO/USsL7ZtRaTrqFjyurOkznhIVBfnxlesBfwnfyim9e+xrpHiEPg4gdS5z4vq0BMkBD4iYGbfIv7Mf0cGAQQQQKAEAXcH1l9Tja1faC9EXg2s7A311xhlvtJeMH4eAQQQiLKAd8Q1ouvGil0zT+yKl9z3LDFv/4O3C+3kUL39fuDezHVBlI+c3PMUyN42Ruym+XmuZhkC8RNIn/64SPfd41cYFZUsYFruE/8p/ppUMiQBEEAg0QJp7V2sGub9rj2EvBpYdlLdRRnf/r69YPw8AgggEFWB3N1XqbMeE/Fqti/BZMSufE3sshlil0wXs+pJEfe/Jf1LdR0hqdPvcI/UfMgr6TBxrH/dYsnceXQcK6MmBPIWSJ00VdSuI/Jez8LkCJhFT4j/2KeTUzCVIoAAAmUQSKfTx6rz50xrL3R+Dawb6o7JKNtusPY24+cRQACBsAp4+7o36e2Xx5v0Mhu33Z21dIaYJdPcq7OfcyXZsJZVpryUpI6bLKp/u4+pl2l/wlZSwMy7W/ynv17JLdkLgdAJpMbeJWq3fUKXFwlVX8Aue16yD55d/UTIAAEEEIiwQLpnt17qtJfXtFdCfg2sR/bu6i/e8K5x/9beXkB+HgEEEIicgE5L6lNPiupc+Lsq7JZ3xS5/wd2d9bT7flTs+o9982vkaHaUsLfXZaIPoqERi8PMowjmX+WBxJLYC6ROvFNU731jXycFFi5gV78p2aljC7+QKxBAAAEEtgkotaCmsbU2H468Gli5QJnm+tnWmOH5BGUNAgggECUBXTtBvKN+HEjKdsMS97jhc9saWksfEbt5cSBxwxJE9ztRvDHXuH/O8MKSEnmUWYD5V2UGJnwkBFKfvF1Un/0jkStJVliAx6wrDM52CCAQNwGr9V0dGlrOyKeuvBtYbc31k8SYcfkEZQ0CCCAQJYHU2HvdoyF7lSFl92jhu/PFLJ3pvp9yja1H3b8GtHtnbBnyCCak6jlSvE9eL6qmSzABiRJ+Af5iFv4zIsOKCPAIYUWYI7mJ3bBUsrcfEcncSRoBBBAIg4Dy7FXp8Qu+n08ueTewMs3DvmVN5uf5BGUNAgggEBUBvdsx4o39e2XSNb7Y1bPdI4fPiVnm7tBa7t5qlV1bmb1L3EXtcoh4x/+J18iX6Bi1y5l/FbUTI99yCaRPfUikZ325whM3wgJ2wzLXwDo8whWQOgIIIFBdgbSn/k2Nb70lnyzybmDZpvoTMtY8kE9Q1iCAAAJREfCO/ovowcdXJ933G1rLZrm7s54Wk2to+euqk8vH7Kr7jRV9zC/cnVfdQpcbCZVXwH/qP8S0XF/eTYiOQAQEUmc+JapLvwhkSoqVFrAbV0h2Ci81qbQ7+yGAQHwE0p177KnOfvHNfCrKv4F19767+GvXrTJG8r4mnwRYgwACCFRLQHUaLKkz3b+qh2Wek8m6O7TecLOz3AytZU+JWZFraG2oFo8bqOiJt9clovf/SniMqqeRyJ2Zf5XIY6foHQikzn3R3YHaHRsEPiJgN62S7G0HI4MAAgggUISAe0vgBq9xfnelxL0zsP2vgppR2Um1re6Ggdr2w7ICAQQQCL+At/+PRO/zmfAmajJiV7mG1tY7tFxDa/kTImZjRfJVPQ8U77Cr3GywT1RkPzYJn4Bd/7Zk7zgqfImREQKVFnBvqk2Pc2+YVbyMu9L0UdjPblrtGlgjo5AqOSKAAAKhE1BaPZNuaM37OezCGlhNQ2411j87dFWTEAIIIFCogO4sqbOfdP+i3qPQK6u33rRta2gtf1HMyudEVjwpdsvyQPNRPfZzTb2vbXuskr+sBWobtWCm5R7xn/pa1NImXwQCF1Bdhrm7de8PPC4B4yFgN7sG1q00sOJxmlSBAAKVFtCir0lNaLko330LamD5TcO+59tMMO+azzdD1iGAAAJlENBDv+juMPpOGSJXMmTuLYdviVn1qnv08DWxG5eJrHlF7Hp3p0ABX6rLEFEDPym67lR3x9Xe7sqCfmsoYCeWRkmA+VdROi1yLaeA7jPGvYH12nJuQewICzDEPcKHR+oIIFB1Aa3SF6Qa51yXbyIF/S0l27TXKcZuuiff4KxDAAEEwioQ5zdK2TY3N2vNm2LXLRTZtFLsJneXVtsH3nbYaTdRnfuLdO0vqtdebjBx37AeE3lVUSA75TjXFG2tYgZsjUA4BHRto3hHXRWOZMgifALvLpDM3aPDlxcZIYAAAhEQSOvO+6iG117NN9WCGli2ua5vxtil+QZnHQIIIBBGAd3vRPGO/2MYUyMnBMIhsG6xZO48Ohy5kAUCVRbw9vuB6H0vqHIWbB9WAbt6tmSnnhTW9MgLAQQQCK2A1mq9N761pxvg7uebZEENrFzQTHPdYmvsgHw3YB0CCCAQNoHU6InukTmGU4ftXMgnPALMvwrPWZBJ9QW8UdeJHnRs9RMhg1AK5OZSZh84M5S5kRQCCCAQZgGt7aOphgVjCsmx4AZWdmL93UbMqYVswloEEEAgLAKq63BJnTGVAeVhORDyCKWA/9SPxLRcF8rcSAqBSgukT3tEpEdtpbdlv4gI2Lefkewj4yKSLWkigAAC4RHwtP5vr6HlikIyKriBlZk49EdWsj8oZBPWIoAAAmER8Eb+XPSI88KSDnkgEEoB5l+F8lhIqhoCuqOkz39ZRKeqsTt7RkDALHxU/Gk8YhqBoyJFBBAImYBW6rxUY+vNhaRVcAOLQe6F8LIWAQRCJZDqIemznxRJdw5VWiSDQJgE7Pq3JXsHj9iG6UzIpXoCuvco8U68vnoJsHPoBczcO8V/5huhz5MEEUAAgbAJpDvtUqvOeX5BIXkV3MCy9+3dy1+9YaUxvGe9EGjWIoBA9QX0iK+LN/Ky6idCBgiEWID5VyE+HFKruIC316WiD7q44vuyYXQEzMt/E/+l/4xOwmSKAAIIhEBAKbU83dha8KvQC25g5WrNNNfPtsYMD0HdpIAAAgjkKaAkffpjIt0H5bmeZQgkU4D5V8k8d6resYB3zLWi9yhoviyUCRPwZ14tZvbvE1Y15SKAAAKlCVil7+7Q2HJ6oVGKamC1Ne1xvVj9mUI3Yz0CCCBQLQE18FOSGv3ram3PvghERiA75XixG1siky+JIlBOgdRZM0R17l3OLYgdcQH/ie+IWTAp4lWQPgIIIFBZAS8lP/TGzS/49tWiGli2aehXMjZ7TWVLZDcEEECgeIHU8TeL6ndw8QG4EoEkCKxfIpk7jkxCpdSIQLsCqsf+kjrt9nbXsSDZAv6DXxSz7MFkI1A9AgggUKBAusY7WZ03774CLytujpW9ZfCBmc3quUI3Yz0CCCBQDQHVfR9JnX6n27qonn01UmZPBKoiYFruFf+pi6qyN5siEDYB5l+F7UTCmU/27jPEvuveVMkXAggggEBeAlqL9brt2kedPmtlXhd8YFFRf5uzVjx/Ut07xtiuhW7IegQQQKDSAt5hvxY99FOV3pb9EIicgP/Uf4pp+Vvk8iZhBMohkDruJlH9DylHaGLGSCBzs7u7u21VjCqiFAQQQKC8AlrpeanGlqHF7FJUAyu3UbZ58CPGqNHFbMo1CCCAQKUEVE1vSZ01TSTVsVJbsg8CkRVg/lVkj47EgxZI7yLpc54W8WqCjky8GAnYtvWSvXnfGFVEKQgggEAFBLSeXNPQMr6YnYpuYGUm1v7Miny7mE25BgEEEKiUgLf3v4s+4KuV2o59EIiuAPOvont2ZB64gK7/nHhH/DDwuASMl4Bd/aZkp46NV1FUgwACCJRZwNNyqdcwv6i3axXdwLKT9/xUJruFyZZlPlzCI4BACQIqJalPPS6qS78SgnApAskQYP5VMs6ZKvMTSI1uFjXwiPwWsyqxAmbho+JPuyCx9VM4AgggUJSAMgfXNL41q5hri29g/WNIn8wKf1kxm3INAgggUAkBvcf54h3zX5XYij0QiLyA/7SbfzWP+VeRP0gKKF2gZlf3+OBTIk2TX8IAACAASURBVDpdeiwixFrAvN4s/nPfjXWNFIcAAggEKaC1Wu+Nb91FKckWE7foBlZus+zE2hYjUlfMxlyDAAIIlFsgdeKdonozm6LczsSPh0D29hPEbpgXj2KoAoESBPSIi8UbeWkJEbg0KQL+jJ+LmfN/SSmXOhFAAIGSBVwD64FUQ+uJxQYqqYHV1lTbJFYait2c6xBAAIFyCeheh4l38uRyhScuArESsBuWSPb2I2NVE8UgUKxA+rRHRHrUFns51yVIwH/4q2KWTE1QxZSKAAIIlCbgpeSH3rj5/1lslJIaWLZ5yNczxv9tsZtzHQIIIFAuAe/I/xNdx2DVcvkSN14CpmWq+E/xsoN4nSrVFCOg+x4v3gl/KeZSrkmgQHbKcWI3tiawckpGAAEEihNQXs0J6fFvPlTc1SKlNbBuGXxgZrN6rtjNuQ4BBBAoh4DqOFBSZ7l/QWd+STl4iRlDAeZfxfBQKakoAW/UdaIHHVvUtVyULAHbtkGyN++TrKKpFgEEEChBQCuV9QZ03kWNeXV9sWFKa2BZ0dmmulVWbM9iE+A6BBBAIGgBb7/vid7380GHJR4CsRXI3n6im381J7b1URgC+QiobntJ6vS73T/v6nyWsybhAnblK5L9x+kJV6B8BBBAIH8BpcyMdONbh+V/xUdXltTAyoXb0lR/l7LmtFKS4FoEEEAgMAF311XqzKdEddo1sJAEQiDOAsy/ivPpUlshAt4R14iuP7mQS1ibYAEz9w7xn7kkwQKUjgACCBQm4KW8q71x8y4v7KrtV5fcwMo01V1urf1FKUlwLQIIIBCUgK7/rHhH/EdQ4YiDQOwFmH8V+yOmwDwEVNcR7u6ru9yj56k8VrMEARF/5v+Imf2/UCCAAAII5CmQrrFnq/MWTMlz+Q6XldzAaptcf4hkzYxSkuBaBBBAICiB1En3idp1z6DCEQeB2Asw/yr2R0yBeQh4o/7mZl+NzmMlSxDYJuA/8Hkxyx+GAwEEEEAgDwGtxXq7ev3U2HnL81i+0yUlN7CslZTv5mAZsd1LSYRrEUAAgVIFdJ/R4n3yb6WG4XoEEiXA/KtEHTfF7kBA9x4l3onXuZ8p+Y/F+CZGwLoB7oeJbVuRmIopFAEEEChFQCl5I904f69SYuSuDeR36uwNtVONkpNKTYbrEUAAgVIEvGOuFb3HmFJCcC0CiRKwG5ZK9vYjElUzxSKwnYDyJHXiXaJ2K/nP1MAmSMCuf1uydxyVoIopFQEEEChNwL2B8C+pxtYvlhYloAaWP3not/1s9melJsP1CCCAQLECqlOtG97+oJtf4hUbgusQSJwA868Sd+QU/CEBvefXxDv4m7ggUJCAmX+/+NMvLOgaFiOAAAJJFtAqfUGqcc51pRoEcgdW5qYRR9i2zU+WmgzXI4AAAsUKeAdeJfoTjcVeznUIJFLAf/rHYuZdm8jaKRoB1WmQeKdNFVXTBQwEChLwZ17tBrj/vqBrWIwAAggkWSDdJTVUnTV3XqkGgTSw7Mwvpf037l9jRPgTQKknwvUIIFC4gNdFUmc/6f4Swii+wvG4IskCzL9K8uknvXYlqeMmi+p/aNIhqL8IAf++CWJWTS/iSi5BAAEEkiegtF6cbmjZPYjKA2lg5RLJNtU/YKw5IYikiIEAAggUIqCHXSjeod8u5BLWIpB4AeZfJf4jkGgAb69LRR90caINKL5IAdMmmRv3EzFbigzAZQgggEDCBLS6vqah9XNBVB1YA8tvGvY932Z+HERSxEAAAQTyF1CSPs29xrpHbf6XsBIBBMS03Cf+U19BAoHECeheh4k39gY3MzGduNopuHQBu/I1yf7j1NIDEQEBBBBIiEDaS31GjZ/rfuMt/SuwBpa9oe6YjLLTSk+JCAgggED+ArrfSeId/4f8L2AlAghsFfCfvsrNv/orGggkSkB16CfeybeJ6tI/UXVTbHAC5o2bxJ91RXABiYQAAgjEXCDdKTVInTN3URBlBtfAmnNyB3/GG2uMtZ2CSIwYCCCAQD4CqTGTRA04PJ+lrEEAgQ8IZO8YK3b9m5ggkBwBd8dV6ribRPU9IDk1U2ngAtlpl4tdeGvgcQmIAAIIxFFAaXk93TD/E0HVFlgDK5dQtrnufmPsJ4NKjjgIIIDAxwmobiMkdfo9Iu5XRr4QQCB/AbthmWRvp/Gbvxgr4yDgHf470UNOi0Mp1FAtAWskc4sb/N+2qloZsC8CCCAQKQGt1O9Tja1fDyrpQBtYmRvq/t0q+99BJUccBBBA4OMEvIN/IXrPc0FCAIECBcz8B8Sf/qUCr2I5AtEV8A78sehPTIhuAWQeCgG7erZkp54UilxIAgEEEIiCgE7XnJU6/83bg8o10AZW2017HyBtG54PKjniIIAAAjsVSO8i6bOeEEl3BgkBBIoReHe+mKWzxCx7WuyyR7ijoBhDromEgLf3FaIP+HIkciXJcAuY15vFf+674U6S7BBAAIGQCGgtvte9W2912strgkop0AaWtaKyk2rfdnfX9gsqQeIggAACOxLQe10i3kHfAAcBBIIQcL9x2zXzXCPrWTErZold6hpamcD+rBFEhsRAoCgBmldFsXHRTgSyj3xd7Nt344MAAgggkIeA0uqZdENroDMrAm1g5Wpoa66bKMY25lEPSxBAAIEiBZSkz3hcpNvAIq/nMgQQ+FgB44t9p2VrQ8suecLdpfWwiNkCGgKREvD2+4HofS+IVM4kG2IB9+ti5uaD3NDftSFOktQQQACB8AgoL/XT9Pi5gd62GngDy944/LOZTNt14WEjEwQQiJuA2v0sSR37y7iVRT0IhFcgs1HsipfcnVkzxCx53DW3ZrlcbXjzJbNkC+gO4h32S9H1pyTbgeoDFbArX5XsP3gJQKCoBEMAgVgLqBrv+PR589y/ggb3FXwDa9KeA3y7ZZExEnjs4MomEgIIRFkgdcJt7jXoB0a5BHJHINICdstascufd02tF0SWPyNm1VORrofk4yOgOvQR75g/ud8j9o9PUVQSCgHz8p/Ff+mnociFJBBAAIGwC7i3D2726vr1Ukc+tSnIXMvSZMo0177ixmnsHWSixEIAAQRyAqrngZI69dbcfwIEAQRCImA3LHOPGz733iOHj4jdND8kmZFGkgT0rkeIPuZqUV36J6lsaq2QgD91nJjVz1RoN7ZBAAEEoi2glX4w1djyyaCrKMvfAP2mwb/0rbo06GSJhwACCHiH/070EG7h55OAQJgF7IYl7u6sl7fdpbX4frEbW8KcLrnFQEAPuUC8Q74t4tXEoBpKCJuA3bRasrcd7NLi0emwnQ35IIBAOAW8VOpKb9zc/wo6u7I0sLKTB59ssureoJMlHgIIJFsg92hI6qxp7i8oHZINQfUIRE3g3fluELxrZi17xn0/KnbLsqhVQL4hFVCdBrl5Vz8TNfCokGZIWnEQMPPuEv/pi+NQCjUggAAClRHwOhxSM372zKA3K0sDy/59vy6+t36VsYa/ZQZ9YsRDIMECvA49wYdP6fERcDMG7LutWx85NK6hJcufoKEVn9OtaCW6/nOiR14mqqZbRfdls+QJZKddLnZhbnwBXwgggAAC7QkoUStTja39lBK/vbWF/nxZGli5JLLN9Q8bY8YUmhDrEUAAgR0K6LSkPjVdVOfeACGAQMwEtnvkcIm7Q2v9GzGrkHKCFFDd9xHv4O+L6n9okGGJhcCOBUybZG5yjw/66xBCAAEEEMhLIDWxZsLcT+e1tMBFZWtg+c21V/pGeFVHgQfCcgQQ2LGAHjxevKP5JYXPBwJJELAblrsZWi+6bzcYfvnTYt950ZXN7JkknP3H1ahqeove95uih58ror2kc1B/hQTskhmSffj8Cu3GNggggED0BbSkG1MT5jSXo5KyNbDamvYYKVYH/sxjORCIiQAC4RdIjb1b1G683DT8J0WGCAQvYLesFVnphsLn3nS44ln3JrCnRUwm+I2IGE6BVHfx9rpI1IgG97hg13DmSFaxFfCf/W8xb/4htvVRGAIIIBCkgNbie3369FUnzFgVZNz3Y5WtgWWtqOykukXW2AHlSJyYCCCQHIHcq9G9k8rSxE8OIpUiECcBv03smjliV70mduVL7nsmjx3G6XzfryXdS7w9v+QaV+eL6tAzjhVSU9gF3My+7JRjxW5aFPZMyQ8BBBAIhYDy9JPp8S1le7NK2RpYOb1sU91fjLWfD4UkSSCAQGQFvKP+KLr2xMjmT+IIIFB+AbvlXZFVb7im1itiXFNLVrlHD7csL//G7BC4gOo6wj0m+GnRQ88USXcOPD4BEchXwC5/UbIPuM8hXwgggAACeQl4Ov09r2HOT/JaXMSi8jawbhx+psm0TSkiLy5BAAEEtgqojgMlddYjbt5JGhEEEECgIAG7abXImjfdHK0Xtj16uMq99dDfUFAMFldIQHcWPehTrml1rqh+B+Z+9a/QxmyDwM4F/Gd/4R4fvAYiBBBAAIE8BdId7UHq3AXP57m84GVl/dOB/ft+XXxv3Up3F1bHgjPjAgQQQMAJePv9wA3tvQALBBBAoHQBk3UD4Vvdt3v8cI170+HqV8WscX/GyqwpPTYRihLQux0lash5ogefwN1WRQlyUdkEtj4+ONo9PriwbFsQGAEEEIiTgBK1JNXYOlCp8r15p6wNrNxhZG+onWqUnBSng6EWBBCokIDu6O6+etLdhbVLhTZkGwQQSJ6AdfOzlriG1lx3t9YbYt6Z7X50s7XWux/L9+ev5DG/X7HuILrvcaIGjhE94EiRbgOTa0HloRbg8cFQHw/JIYBACAW0Un91DawvlDO1sjew7KS6izK+/X05iyA2AgjEU0APuUC8w38Qz+KoCgEEwi2Q3Sx27QJ3t1aLyLstrrHl7th6xzW2Nrr/zldBAqrT7q5hdbKoAUeL7ncwd1oVpMfiagn4s34p5o3fVWt79kUAAQQiJ5BW6XNU45zbypl4+RtYN43cI9O2akE5iyA2AgjEUyB1yv2idhkWz+KoCgEEoimQ3SR23aKt37L2Lfdjq2twzRWzzjW42sryxuhoObl5hbqHm2HVe6SoXfcTvdsnRLoPcjWU/Y+c0XIi23AL8PhguM+H7BBAIHQCWumMl96ltzpvlnurTvm+KvKniUxT/UvWmn3LVwaREUAgbgK67/HinfCXuJVFPQggEGMBu+UdkXVvi92wVGSDeyxxg2t0rXeNrg25Rpd7RNFsjFf16V6iu48Q6TFMVM89Re22t/tHh+EiKUafxuugk1eNXTpLsg+dm7zCqRgBBBAoUsA1sB5ONbYcX+TleV9WmQZW89CfWpO9Mu+sWIgAAokX8EZd795INSrxDgAggEB8BLa+FXHTCve9WuzmlSIbV7ofl7sh0ctFbVzmHk9cLHaLa36ZzeEo2s0hVB0HiOqyu9iOvUV3Hezuphosqlvux0GiOvUKR55kgUDAAv6TPxTT+veAoxIOAQQQiK+AkprL0xPevLrcFVamgXXDkCOt8qeXuxjiI4BAPARUlyGSOuMfItqLR0FUgQACCBQikJu/1bZeVGada2itdf95nXs8Mfft7srPbBDxM2J9dzeX+1Gy7kfj/rv7UZm29ndJdXMzqDq5V7x2ElXj/rPn7pbK3TG19T+7/61Tb9e06inWNae2/jxfCCRNILNRMrce6v7/5f6/xhcCCCCAQF4C6VTXT6hxr7ye1+ISFlWkgWWteNnmuiXW2t4l5MqlCCCQEAHvoJ+K3mt8QqqlTAQQQAABBBAIi4CZe6f4z3wjLOmQBwIIIBB6AXfPwfzU+Pl1lUi0Ig2sXCFtTXtcL1Z/phJFsQcCCERYwOsmqbOfcP/y3z3CRZA6AggggAACCERRwL//s2JWTIti6uSMAAIIVEXAk/SvvQlzLq3E5hVrYGWb6v7NWHtTJYpiDwQQiK6AHv4V8Q75VnQLIHMEEEAAAQQQiKbAusWSufMYl7uNZv5kjQACCFRBIJ1OH6vOn1ORzn/FGlj23l7d/TU9VxhraqpgypYIIBAJASXp0x91w4H3iES2JIkAAggggAAC8REwL/yf+K/+PD4FUQkCCCBQZgGl1IpUQ2t/pcQv81Zbw1esgZXbLHtD7VSj5KRKFMYeCCAQPQE14DRJjfld9BInYwQQQAABBBCItoA1kr39BPc20NZo10H2CCCAQAUFtFJ/STW2frFSW1a2gTV56BdMNvvnShXHPgggEC2B1HE3iurv3vzDFwIIIIAAAgggUEEBu2SGZB8+v4I7shUCCCAQfQGtOp2aanz93kpVUtEGlr1r5G7+2tVL3CysVKUKZB8EEIiOgN71KFH9jhTVZ6So3vu6V713jk7yZIoAAggggAACkRXwp10hZiHjeiN7gCSOAAIVF9BarfP6H9tHjbluc6U2r2gDK1dUtrn+YWPMmEoVyD4IIBBRAZUS1WNf18g6zN2Vdbiovq6pVdM1osWQNgIIIIAAAgiEVcBuWCbZO452s9uzYU2RvBBAAIHwCWg9uaahZXwlE6t4A8tOqrso49vfV7JI9kIAgRgIfKShdZBraHWLQWGUgAACCCCAAALVFDDP/178166uZgrsjQACCEROwOoO53domF3RW1cr38C6p7afv0YWGxEduRMiYQQQCI8ADa3wnAWZIIAAAgggEFUBf4tkp4wSu2V5VCsgbwQQQKDiAlrpLd7m/r3V56evq+TmFW9g5YrLTKqfbn1zZCULZS8EEIi5AA2tmB8w5SGAAAIIIBC8gJl7h/jPXBJ8YCIigAACMRawWt/VoaHljEqXWJ0G1uThl9lsG/fpVvq02Q+BJAkoz83Q2u8DM7QOdI8cdk+SALUigAACCCCAQDsC2XvOFfvOLJwQQAABBAoQ0Fr/v1RDy98KuCSQpVVpYNlJB9T69p0WY6Qq+wciRxAEEIiWAA2taJ0X2SKAAAIIIFBmAbt0lmQfOrfMuxAeAQQQiJeA1uJ7PVP91SlzV1S6sqo1kPzmupm+sSMrXTD7IYAAAu8LqK4j3BsOR7/3lkPu0OKTgQACCCCAQJIEso9dJnbRlCSVTK0IIIBAyQLu7qtH3N1Xx5UcqIgAVWxg1V7pG/lpETlzCQIIIBC8QO4OrZ7uzYb9jhLV52D3vS+PHAavTEQEEEAAAQRCIWDXvy3ZO0eJWD8U+ZAEAgggEBWBtPYuVg3zfleNfKvWwLK37T88s/Hd2dUomj0RQACBfAS2u0Orj7tDqwMztPJxYw0CCCCAAAJhF/Bn/JeYOX8Me5rkhwACCIRKwD0+aD3lDVbj5y2sRmJVa2Dlis1MHPKyFX+fahTOnggggEChAjS0ChVjPQIIIIAAAuETsBtXSPaOY0TMlvAlR0YIIIBAiAWUVU+kP93qfgGtzldVG1j+pNof+r78R3VKZ1cEEECgNAEaWqX5cTUCCCCAAALVEPCf/YWYN6+pxtbsiQACCERaIK28r6vGeb+vVhFVbWDZO4YMzazz51SrePZFAAEEghT4Z0Mr97hh35GiOu0aZHhiIYAAAggggECJAnbTasnenrv7amOJkbgcAQQQSJbA1rcPihqoGlqXVavyqjawckVnmuuetcYeXC0A9kUAAQTKJbBdQ6uPGxDfebdybUVcBBBAAAEEEMhDwJ/1SzFvVGX2cB7ZsQQBBBAIr4AWdX9qQuvYamZY/QbW5OGX2Wzb1dVEYG8EEECgEgI0tCqhzB4IIIAAAgjsWMBuzt195d486G+ACAEEEECgQAHt6c+nxrdcW+BlgS6vegPL3npMf3/LwoXGiBdoZQRDAAEEQi6wfUPLPXbYuXfIMyY9BBBAAAEEoitgnvut+K//KroFkDkCCCBQJQGtdJu3a6f+6qRXV1cpha3bVr2BlUsi21T/kLHmuGpCsDcCCCBQbQEaWtU+AfZHAAEEEIirgN28xt19day7+2pdXEukLgQQQKBsAkrUHekJrWeWbYM8A4ejgTV56BdMNvvnPHNmGQIIIJAIARpaiThmikQAAQQQqICAP/N/xMz+3wrsxBYIIIBA/ARsyhvfYdy8ydWuLBQNLDtldE9/41tL3V1YHaoNwv4IIIBAWAW2NrT6HiGqt3vcsN9horr0CWuq5IUAAggggEB4BNYulMw9x7s3D2bCkxOZIIAAAhER0Eo2egO69FVjXl1f7ZRD0cDKIWSbaqcYK1W/Ja3aB8L+CCCAQL4CNLTylWIdAggggECSBbKPXSp20e1JJqB2BBBAoHgBrSfXNLSMLz5AcFeGpoG1pXnP85TZcmNwpREJAQQQSJbA9g2tQ90dWn2TBUC1CCCAAAIIfEjALntRsg/yb+R8MBBAAIFiBdKpDmeqcbPvKPb6IK8LTQPLPvK5jv6SaUuNMT2CLJBYCCCAQFIFVPd93COHR7lv18zqe4Cojr2SSkHdCCCAAAJJFLBG/KnjxayZkcTqqRkBBBAoWUCLWusNPNY9Pnjd5pKDBRAgNA2sXC1tTXtcL1Z/JoC6CIEAAggg8CGBfw6F73+4a2i5OVo13TFCAAEEEEAgtgKm5V7xn7ootvVRGAIIIFBuAU/rv3kNLf+v3PvkGz9UDSx705CTMm3+1HyTZx0CCCCAQPEC2ze0DnYNrS7FB+NKBBBAAAEEwiSQ3STZO8eK3bQwTFmRCwIIIBApgXQqNVaNm3t/WJIOVwPLSirbXLfIWsvglrB8QsgDAQSSIaDTond1bzjc+siha2b13lvE48WwyTh8qkQAAQTiJ2Be/qv4L10Vv8KoCAEEEKiQgNKyNDV+/iClJFuhLdvdJlQNrFy2bZPqfiO+vbjdzFmAAAIIIFA+gVxDaxfXyOo3yn27GVp99hHRNeXbj8gIIIAAAggEJbBusWTuPlHEbAwqInEQQACBxAm4xwf/2z0+eEWYCg9dA8veMvjAzGb1XJiQyAUBBBBIvIDXxd2hdZi7M+uQ9xpa+7mGVirxLAAggAACCIRMIDe4/aEviVn2UMgSIx0EEEAgWgJp6bifmvDGy2HKOnQNrByO31T3vG/tAWGCIhcEEEAAgQ8IpHu5Rw2PFd3vCNF9R4r0rHM/GcrfUjg2BBBAAIEECZi5d4r/zDcSVDGlIoAAAsELKGVmpBvfOiz4yKVFDOXfNvym2m/4Vn5dWmlcjQACCCBQKQHVoZ9InyNF9x7pGlvuLq1eQ2loVQqffRBAAAEEtgrYTaslm3t0sG0VIggggAACJQikU/oiNa7lmhJClOXSUDaw7IOH7uovW7nYWMME4bIcO0ERQACB8gqoToPc3KyjRfV3g+FzM7S68G6O8ooTHQEEEEDAn3aFmIU3AYEAAgggUIKAVrrN67vbAHXCjND9a0AoG1g562zTkFuN9c8uwZ1LEUAAAQRCIqC6jnDNrNHu+3DX2DpQVIfuIcmMNBBAAAEE4iBgFk4Tf9pn41AKNSCAAAJVFlA31UxoPb/KSexw+/A2sG6sO91k7J1hRCMnBBBAAIESBJQnuqd71LD/Mdvuzuq9r0iqUwkBuRQBBBBAIMkCtm2D+Hef6h4hXJBkBmpHAAEEAhHQqtOpqcbX7w0kWMBBQtvAslZS2aa6t6zY/gHXTDgEEEAAgTAJqJToXrm3G4567w2H+7g3HNaEKUNyQQABBBAIsYD/5A/FtP49xBmSGgIIIBANAaXUslRD6+5KSTaMGYe2gZXD8icN+4XvZy4PIxw5IYAAAgiUScDrIrrPKNF7NooaeFSZNiEsAggggEAcBEzLfeI/9ZU4lEINCCCAQNUFPK3/22touaLqiewkgVA3sOxNe3wi06ZfDSseeSGAAAIIlE/AO+w3ooeeUb4NiIwAAgggEGkBu/5tyd57mkhmTaTrIHkEEEAgLAJp6bifmvDGy2HJ58N5hLqBlUs2M3HIDCv+IWEFJC8EEEAAgfIIpE9/XKT77uUJTlQEEEAAgWgLGF/8By4Qs9L9XsEXAggggEDJAkqZGenGtw4rOVAZA4S+gWWbhn4lY7PXlNGA0AgggAACIRNQnQZJ6uxpIcuKdBBAAAEEwiJgnv+9+K9dHZZ0yAMBBBCIvEA6pS9S41pC3XsJfwPrppE9/MzqJcZaXlEV+f9LUAACCCCQn4CunSDeUT/ObzGrEEAAAQQSJWCXzpTsw+NErJ+ouikWAQQQKJeAVrrN67vbAHXCjFXl2iOIuKFvYOWKbJtY2+x+GB9EwcRAAAEEEAi/APOvwn9GZIgAAghUQ8BuWi3+1DPFblpYje3ZEwEEEIingNaTaxpaQt9ziUQDKzNpn9HWX/9IPD8pVIUAAggg8GGB9Onu8cHug4BBAAEEEEDgXwKmTfwHvyhmBY+Y87FAAAEEghRQXtcx6fGvPBpkzHLEikQDK1d4prn2FWtk73IgEBMBBBBAIDwCquNASZ2TG8obmd+iwoNHJggggECMBfxnfiZm7p9iXCGlIYAAApUXUFrNTo1v3UspsZXfvbAdI/O3A7+59hLfyK8KK4/VCCCAAAJRE9C1jW7+1VVRS5t8EUAAAQTKKGBenyT+c98p4w6ERgABBJIpoFI130yPe/OXUag+Mg0sO2V0T3/j/MXGSucowJIjAggggEBxAt5hvxY99FPFXcxVCCCAAAKxE9g2tN2NZrHZ2NVGQQgggEA1Bdzw9i3eLnqQOmXuimrmke/ekWlg5QryJ9X/zffN5/ItjnUIIIAAAtETSJ/+mJt/tUf0EidjBBBAAIHgBdYuksw/znRvdQr1i7GCr5uICCCAQEUEUhNrJsz9dEW2CmCTSDWw7OShh2ey2acCqJsQCCCAAAIhFFCddpfU2bnhvJH67SmEkqSEAAIIRF/Atq0T/x8NYte+Ev1iqAABBBAIoUC6puYYdd6bT4QwtR2mFLm/IfhNtbN8KwdFBZg8EUAAAQTyF9CDG8Q7+if5X8BKBBBAAIF4CvhbxH/4q2KWPxzP+qgKAQQQqLKA0vJ6avz8vaMwvP19qsg1sOzEYV/OSOYPVT5rtkcAxCLLmwAAIABJREFUAQQQKIOAd+ivRA9zj4rwhQACCCCQXAGTley0y8UuviO5BlSOAAIIlFnAU+lveI1zflvmbQINH70G1iN7d/UXb1xsxHYPVIJgCCCAAAJVF0if9qhIj8FVz4MEEEAAAQSqJGCN+NO/L2ZBc5USYFsEEEAg/gJaqU3erp13Vye9ujpK1UaugZXDzU7a4w/G11+OEjS5IoAAAgh8vIDqOFBS5zzuFkXytyaOFwEEEECgZAEr/oyfi5nzx5IjEQABBBBAYOcCnqev88a3XBA1o0j+LcHess9+mc3rX4waNvkigAACCOxcQA8e7+Zf/RQiBBBAAIGECpgXrhH/1V8ktHrKRgABBConkE6ljlDj5j5duR2D2SmSDaxc6ZmJdU9ZsYcHw0AUBBBAAIFqC3iH/tLNvzqr2mmwPwIIIIBAFQTMK9eJ/+KPqrAzWyKAAAIJE7D6pZpPt+wfxaoj28CyNw7/bCbTdl0U0ckZAQQQQOCjAunTHnHzr2qh+aCA8cV/9Ouieh8sqt8honb9hIj2MEIAAQRiJGDFvPBHd+fVz2NUE6UggAAC4RVIp/RFalzLNeHNcOeZRbeB9cjnOmbffuwta23vKMKTMwIIIIDAvwRUxwFu/tUT7n+I7G9LZTlOu+oNyd538r9ie91E9zlaVH/33dc1tHoNxaws8gRFAIHKCLiZV8/9Vszrv67MduyCAAIIJFxAi1rr9Xp3kDpl9dooUkT6bwqZ5sFXWaO+G0V4ckYAAQQQ+JcA8692/Gkwrze7v9zt/Le5XONP9T3WNbSOcHdoHSqqS18+VggggEA0BHJvG3zmp2Lm/TUa+ZIlAgggEAcB7f2mpmHeJVEtJdINLDtpzwG+ybQaa2qiegDkjQACCCAgwvyrHX8Kso9eInbxHXl/RFTXEa6ZNdp9H+4aWweJqumW97UsRAABBComYLLiT/+emLdurNiWbIQAAggkXUBrsV7HHiPU2S++GVWLSDewcuhtE2ub3Q/jo3oA5I0AAgggIML8qx18Ctz8q8wth7q3lqwu7iOiUqJ7HeaaWcdsuzur995ufhb/3lMcJlchgEBgAtlN4j92mZil9wUWkkAIIIAAAu0LWK3v6tDQckb7K8O7IvoNrEl7Hiz+lmfDS0xmCCCAAAIfJ8D8qx3r2FWz3fyrk4L78Hhd3PysUa6R5WZn9d5fVB/38hkGwgfnSyQEEGhXwG5cKWbaxWJWPdXuWhYggAACCAQrkFb6k6qx5cFgo1Y2WuQbWDmuTHPd09bYwypLx24IIIAAAkEI6MHjxDv6Z0GEilWM9uZflVxsehfRu7lHDf85EH5YySEJgAACCOxMwK58TfxpF4rdtAgkBBBAAIEKC7jGz2upxvn7KCW2wlsHul0sGljZG4aNNyqTe5SQLwQQQACBiAl4h1wtevjZEcu6/OlmH7tU7KLby7/RezuoLsPco4buDq2+7nHDvgeK6sxLfiuGz0YIxFzAtEx1A9svFTFbYl4p5SGAAALhFEinzIVq3Ft/Cmd2+WcViwaWtZLKNtW3WjG75186KxFAAAEEwiDA/KsdnIJ7O1fm5kOKn38VwMGqToPdY4ZHvveGQzdLq0ufAKISAgEEEiXgfi0zL/5J/Fd/nqiyKRYBBBAIk4Cn1BrtdxukPvPShjDlVUwusWhg5Qr3bxj8HV+pnxSDwDUIIIAAAtURUB36SercJ93msfntKBBIu9rNv5oa4Pyr/8/encBrVdX7H1+/tfdzmAdxHhAOB3BCUUEtnOJmOWIpCOcccLjZbbLhlo1aRmZlZpa3+mfeNBPOIIoTDlmWI2IOOaICBw44m4qCjOfZe63/OtQ1oYNwztnP8+y99ue8Xr7U27PX+n3fa9OFX/v57QSqkgH7uSezDnV/uRlaO+yvpOc2CazKEggg4KuAbVvl3jR4rrIv3+xrRHIhgAACmRAIrP5xcOqSb2ai2C0U6c2fGOycMdvFK5c/b6zt5cPBkAEBBBDIg4De3c2/Opz5V5uetXm2ScV/OyfVt4D03dM9nfUh95ebo7WD+8phj/6prpfiEECgfAL2jWdd8+oryq56rnybshMCCCCAwL8JaK3iQAYOl7rHl/rA400Dq/0woobq37oG1pk+HAwZEEAAgTwIMP+q41Mu9/yrJO61jRpaOx6opKpfEsuyBgIIZEmg/SuDzzWr+PHpbt5VMUuVUysCCCDgpYAWuTac2jrZl3BeNbBs8+BRsQmeNIbvovhyg5IDAQT8Fiic8BelBlT7HbKz6drnX113sFJtb3b2yvR8XgK14SuH27vZWe1PaO04xjW0+qanPipBAIHEBeya15V54FvKvPbnxNdmQQQQQACBrgkUqqoOl8kL7+/a1em7yqsGVjtv1DjsL8aY8emjpiIEEEAAgfcKSI8dVTjRzb8SDcx7BOzyhW7+1dF+mUio9LaumdU+Q2voR5UMrPErH2kQyLmAeeFeZR78qrJtr+dcgvgIIIBAegTc8PbHgqmtB6anou5X4l8Dq3nPE0y0bk73aVgBAQQQQKCUAnrwZBUcwZupNjU2z12j4ke9mLPZ4e0THHKp0sNPLOWtxdoIIFAmAbt+hTJ/+7kyS64q045sgwACCCCwtQKFQtUZMmXh77f281n4nHcNLGuVRA01T1oVj8rCAVAjAgggkFeBYOzFSu8xMa/xN5s7uscNPn7xBm9dChPuU6r/bt7mIxgCeREwL9yjzEPnKLvu5bxEJicCCCCQGQHR+qVw9GnDZJ/pbZkpeisK9a6B1Z7ZXjPy9GKx7aqtyM9HEEAAAQQqJFA43s1JGTisQrundFsf5l+9D630GqzCk+9NKT5lIYDAVgmsekXFD12gzCu3bdXH+RACCCCAQPkFRORrhamtF5d/59Lu6GcD65FPFYrP/anFtbJ2Ly0fqyOAAAIIdEVAeuzg5l/NY/7VJnhezr96T0ZdfaoKxp3flVuGaxBAoNICJlZm0Y3uDYPu13C0stLVsD8CCCCAwGYEtJKVQdWg3WXyoyt8Q/KygdV+SMXmkV+xUdtPfTsw8iCAAAI+COjBp7j5Vxf5ECXRDMy/SpSTxRBAICEB+9I817i6SNm3H09oRZZBAAEEECiVgGvyXFiYtvRbpVq/kut628CyV+/XJwreWWat3baSwOyNAAIIIPDvAsHYn7j5V5Og2UTA//lX7uuD/Qdz7gggkBEBu3yBG9L+U2Ve+1NGKqZMBBBAIN8CWvT6oOeu1TLxvld8lPC2gdV+WMXGIRdYI+f6eHBkQgABBLIsEB5/p5KBNVmOkHztuZh/dY9z8/q3HsnfF6yIQAUE7OrXlHnqN8osvsrtbitQAVsigAACCHRFQGv537C+9VNduTYL13j9u0h7R80O8RtmqbG2VxYOgxoRQACBPAhI1fYqnPQg8682OWz71iIV3fZRb28BPXSqCg69wNt8BEPABwG77i1l5l+lzMLfKGXW+xCJDAgggEBuBLT7b+4g7DtKap9+1tfQXjew2g8tatr91ybWn/H1AMmFAAIIZE2A+Vcdnxjzr7J2J1MvAv4IbHjiakGza1xdoVT8jj/BSIIAAgjkSEBLcH04dfFEnyN738Cys0cOi9e3LTRGBT4fJNkQQACBrAgEYy9y869OyUq5ZaszuvdsZV+4vmz7lXujwgTmX5XbnP0Q2KLAiqUqfvZqZVpnuv/dvrjFj/MBBBBAAIH0CkhVz3GFyc+513z7++N9A6v96KKG6lnua4T8acnf+5hkCCCQIYHwuD8p2WZ4hiouQ6m+z7/quasKJ97nIHPx244y3DBsgUD3BOwb81X8zJWuaX6DW4gZV93T5GoEEECg8gIi4T2FqS0fqnwlpa0gF7+TbGvaY6yK1z9cWkpWRwABBBDYokDVtqow6SHmX20C5f/8q2lu/tX3t3h78AEEECihgGlT5qV5yi66RplXbi/hRiyNAAIIIFBuAS29jg+nPntbufct9365aGC1o0YNw/5srPmPcgOzHwIIIIDAvwRk8CQVHvETSDYRMM/NUvGj3/DWJTjkUqWHn+htPoIhkGqBFcvcVwRvV2aRe+Kq7fVUl0pxCCCAAAKdFxAVPO1mX+0n4v8jtblpYNmGYUcVrflT528HrkAAAQQQSEqA+VcdS0b3ftV9lWd2UsypW6cw4R6l+u+eurooCAFvBYprlHn+L+5pqyZl3nzA25gEQwABBBBQqiDB6TJ18dV5sMhNA6v9MIuN1Q9YYz+Yh4MlIwIIIJBGgfC4P7r5VyPSWFrlanLzr6LrPuDtkxHSa7AKT3YNLOZfbXSP2baVSta84Rp7Q5TSvGemcr8APdo5WqfMy+4rgkv/4L4qeIsbyr7Go3BEQQABBBDoWECeL+z5keEy9vJcvIkjVw2sqGGv44xdeyu3PgIIIIBABQQKg1ThFDeOUHQFNk/vlv7Pv5rq5l9dkN4DqFBlZuENKn74K655VVDSf5SSAXu6v0Yo2XYvpQYMUdJn5wpVxraZEnBzreyrf3NPW/1RmWXuLabRikyVT7EIIIAAAt0TKEj4OZna8uvurZKdq3PVwGo/luLMmoesig/KzhFRKQIIIOCHgAw+2c2/+qkfYRJMYRZcq+JHvp7giulaKjjk527+1cfSVVQKqonv/5ZrODRvthJxb26UbUa7v/ZWauBw19yqdo0u9zXMsGcKqqeESgrYtW8q+8pDyr58n3vSys3rpWlVyeNgbwQQQKBiAqLklXDXI4fJ+KvWVayIMm+cuwZW1FR9oontTWV2ZjsEEEAg9wLBmB8rvefk3DtsCsD8qzzeElZFsw9Xdt1LnQ4vvdzTWQPbm1p7uKbWsH/8NXCYa2z16vRaXJARARMru/w5ZV+8zz1tda+bafWgK9xmpHjKRAABBBAolYCbffUFN/vql6VaP43r5q6B1X4IbhbWw24W1tg0Hgg1IYAAAr4KhMe6+VeDmH+10fl6P/9qNzf/6l4XOZe/3dj8L+UVS1XxlvHJ/VKXwH3l0P3aGriX0v1rNgzMl37uaa1+g5X0GpTcPqxUHoHYfS1w+QJl33hKmdfck1av/kWp+J3y7M0uCCCAAAKZENjw9NWwnWpk3Ly1mSg4oSJz+TtK2zDi5KIt+vu6p4RuDpZBAAEEEhNg/lWHlPbtxSq69ajEmNO2kB5Sr4LDfpC2sipej1nk5l895OZfleOnsI1rau3pmlrua4j9hrq/D3V/302pvrsoqepbjgrYYwsCdu1y16x6WqnXH1f29UeUWe6esDK5mMXLvYEAAggg0EWBQNR/B1OXXtrFyzN7WT4bWFZJceawx5WY/TJ7chSOAAIIZEhAdjtJhUdekqGKy1Mq86/K45y2XbY0/6ps9VZtq3Rf9+SWa2xJX/fEVl83d8s1tjY0t3rv4AbMh2UrJRcbuScu1TsvKbNiiVJvL1TmrYVKLX9C2dWLchGfkAgggAACyQi49yG9Gvbbt0YmzMnd62Zz2cBqv23WN+4xWcz6a5K5hVgFAQQQQOD9BIIxF7r5V1NA2kQgvvfryrxwrbcuhRPu3vBGPX7eK9D1+VdldZTQNbHa34bozq+Pa2z12lGp3ju5f97R/fP27p+3d3/fxpWU299Kbv444vVKrXpF2VUvK/vOC8q+7RpUbz/jGlZPuCercvdnjbLetmyGAAII5EEgCPRXgrolP8tD1k0z5vZ3HdYqHTXUPOHeSDgqjwdPZgQQQKCcAuGxd7j5VyPLuWX692qffzX7g8qu/3v6a+1ChdKL+VcdsiU9/6oLZ5PYJbrHP5pcvXdVqsd2rqm1g5IebuZWT/dXr+2U9NzG/ftA98/u3z0aMm/Xr1Cy7i03hN/9teZ116x60TWrXnR/X+oaVi3Krn0hMWIWQgABBBBA4L0CbvbVG+Guvatl/PxVeZTJbQOr/bCjmSPqjSo25PHgyYwAAgiUTYD5Vx1SM/+qbHdgqjYq6/yrNCUP+rhmlvtaYtWgDU0tWzXQzeDq7/59gJJCP/f39n92fw97u3/v4/5e5b7C6Bpkhd7Ktn+V0f3fJCgoFfTofioTKRu5N463f6UvWu0ey3d/Bii6J6OiVcoW3b//319t7t/XvemazG8oWfuaa1a5p6rWuzdHMp+q+2fACggggAACXRIQka8VprZe3KWLPbgo1w0s9xRWEDVVz3dvJNzDg7MkAgIIIJBKAdnt427+VS6fcn7f8zALrlPxI19L5ZklUVRw8M+UHvHxJJbyao34/nOUWdbkVaayhwld00u7BldHP1XtX2vU//hPYteMil2jyv3Y2DWo2v+dHwQQQAABBDIq4JpXb4a79B6a16ev2o8t1w2sDb+haag5rWjj32f0HqZsBBBAIPUCwZgfuflXtamvs9wFMv+q3OJp2M/Nv7r+CPcVM/d1M34QQAABBBBAAIFOCEgQfrNQ1/LjTlzi3UdpYFkVuqewnuYpLO/ubQIhgEBKBMJj/+DmX/Gg60bHsWH+1Tj3daTXUnJKyZYhPXdV4cT73KK5/23GxrA+zb9K9pZhNQQQQAABBBB4H4ENT1+t26Vazpz7Tp6h+J2lO/31TUOnSKya83wjkB0BBBAoiUBhG1WY9LD7Rk9QkuWzuijzr7J6ct2rO7fzr7rHxtUIIIAAAgjkXiCw9tzg1GU/zDsEDSx3B7hZWGIaqx+NrT0g7zcE+RFAAIEkBZh/1bEm86+SvMuysxbzr7JzVlSKAAIIIIBAWgQ2vHlw/S7D8v70Vft50MD6510ZNe95gonWzUnLTUodCCCAgA8CwYE/VHqvOh+iJJohvvcbyrwwK9E107RY4YS7lRowJE0lpaAW5l+l4BAoAQEEEEAAgcwJBFp9Oahf+vPMFV6CgmlgvQe12FB9j7X2iBI4syQCCCCQS4HwGDf/alvmX218+K6RcV37/KtXvbwnmH+1mWNl/pWX9zuhEEAAAQQQKKWAaP1SOHTHETJu3tpS7pOVtWlgveek7Izqw4ti783K4VEnAgggkGoB5l91fDxvL1HFWz+c6qPrTnF6SL0KDvtBd5bw8lrmX3l5rIRCAAEEEECgpAI6CD8Z1rVcUdJNMrQ4DaxNDitqrP6jMfYjGTpDSkUAAQRSKSC7fkyFH+Jp500Ph/lXqbxdS14U869KTswGCCCAAAIIeCUgoheF9Uv2FlGRV8G6EYYG1iZ4bU17jNV2/UPGMB+sG/cVlyKAAAIqOPAHbv5VPRKbCDD/Ko+3BPOv8njqZEYAAQQQQKA7Alb3mNKjfoG/Q1O7gEMDqwO0qGHoDcaqj3fBk0sQQAABBP4pEB5zu5t/tSceGwn4Pv9qFxVOvN8l5rcXGx37imWqeMuH+LWAAAIIIIAAAghsnYDVTxamLTnAPX1ltu6CfHyK32F2cM62ce99YrPmSXen6HzcBqREAAEEEhYIB6jCKY+6/xYNEl4448utaHWNjP/IeIjNl6+H1Ln5Vz/0Nl9Xg5lFN6r4oS939XKuQwABBBBAAIGcCejQHhfWLrs9Z7G3GJcG1maI2mYOn6FUNG2LgnwAAQQQQODfBJh/1fFNYRbMVvEjX/X2jgkOvkTpESd5m6+rweL7z1VmWWNXL+c6BBBAAAEEEMiRgIjMLUxtPSxHkbc6Kg2szVDZpv2HxmblAmNN1VZr8kEEEEAAgQ0CzL/q+EaI7/umMs9f4+1dUjjhLqUGDPU2X9eCMf+qa25chQACCCCAQD4FJOg7vlD39N35TP/+qWlgvY9P1LT7r02sP8ONgwACCCDQOQHmX3Xkxfyrzt1Fnnya+VeeHCQxEEAAAQQQKL2AluD2cOri40q/UzZ3oIH1Pudm76jZIX7TtBhj+2XzeKkaAQQQqIAA8686Rl+x1M2/Gl+BAynPlsy/6tiZ+Vfluf/YBQEEEEAAgawLaK1s0KPXWJn47N+ynqVU9dPA2oJssWH4dGuj75bqAFgXAQQQ8E1AdjlRheMv9S1Wt/OYhder+OGzu71OWhdg/lXHJ8P8q7TesdSFAAIIIIBA2gRkVtW01ilpqypN9dDA2sJp2Kv36xPpdxZZZXdO08FRCwIIIJBWgeCAC5Tee2pay6tYXfF933Lzr5ortn+pN2b+VUfC7fOvjlR27Qul5md9BBBAAAEEEMiwgBZdDHr030cmPb4owzFKXjoNrK0gjmbu/l9G6cu34qN8BAEEEMi9QHj0bUq22yv3DhsDuEbG7MOVXfeSly7ScxcVTrzfZeO3FRsd8MrnVXHOkV6eOaEQQAABBBBAIDmBQBV+Hkxb9OXkVvRzJX6nuRXnaq0Kooaax62KR23Fx/kIAgggkF8B5l91fPbez7+qVcFhP8rvfb+Z5KblJhX/9b9xQQABBBBAAAEENisQiLyld9x+hBz10Jswvb8ADaytvEOihr2OM3btrVv5cT6GAAII5FJAdjnBzb/6RS6zv19o7+dfHXSJ0iNP4tw3EYjnfluZpQ24IIAAAggggAACmxWQsOrsQu3CSyDasgANrC0bvfuJqLH6j+6NhB/pxCV8FAEEEMiVQHDA9938q2m5yrw1YZl/tTVK/n0muv5Dbv7VMv+CkQgBBBBAAAEEEhHQSrUGh+y1l4y4fX0iC3q+CA2sThywbR45Oo7a/maUcvcZPwgggAACmwqER9/q5l/tDcxGAsy/yuUNwfyrXB47oRFAAAEEEOiMQCGQU6Su9brOXJPnz9LA6uTptzVWX6WMPb2Tl/FxBBBAwH+BoJ8qTP6ba/GH/mftTELmX3VGy5vPMv/Km6MkCAIIIIAAAiURECUPhlNbx4koW5INPFyUBlYnD9XOGrlrXGxbaKzq3clL+TgCCCDgtYDscrybf/VLrzN2JZxZeIOKH/5KVy7NxDUB8686PCfmX2Xi9qVIBBBAAAEEKiKgtbI26HF4YcqCuRUpIKOb0sDqwsEVm4b/wMbROV24lEsQQAABbwWC/c9Xep9Tvc3X1WDx/d9SZllzVy9P/XWFE+5SasDQ1NdZ7gKZf1VucfZDAAEEEEAgQwJaN1fVL6nLUMWpKJUGVheOwV5xaL+o58uLrLU7duFyLkEAAQS8FGD+VUfHyvwrL2/2LYVa+YIqzjliS5/iP0cAAQQQQACBHApo0W1Bb723nNSyOIfxuxWZBlYX+WzD8M8WbfT/ung5lyGAAAJ+CTD/quPzZP6VX/f5VqYxLTer+K9f2spP8zEEEEAAAQQQyJNAEBQuDuoWfS1PmZPKSgOri5LWqqDYUP2oUnZ0F5fgMgQQQMAbAb3zcSr4j195kyepIGaRm3/1EPOvkvLMyjrx3O8os3RmVsqlTgQQQAABBBAok4CILA+37T1Cjpm/vExberUNDaxuHGexadSHbLzKDf/gBwEEEMi3APOvOj7/+P5z3PyrJm9vDuZfdXy0zL/y9pYnGAIIIIAAAt0SKOjgi1K/+BfdWiTHF9PA6ubht82svsY9hTW5m8twOQIIIJBpgfDoW5Rst0+mMyRfvJt/df0Ryq59MfmlU7Ci9NhRhRMfUEp0CqpJUQnMv0rRYVAKAggggAAC6RFwv2V6Nhz50dEy9vJieqrKViU0sLp5Xnb28N3itdFzRqk+3VyKyxFAAIFsCmyYf+W+Ua0L2ay/VFX7Pv9q91oVHP6jUulldl3mX2X26CgcAQQQQACBkgoURB0jU5feUdJNPF+cBlYCBxw3Dz0vjtT3EliKJRBAAIHMCeidj3Xzr3inxaYHZxbd6OZffTlz57m1BQcH/VTpkSdv7cdz8znmX+XmqAmKAAIIIIDAVgvowF4X1i07Zasv4IMdCtDASuDGsHed0TN+6e5n3FNY1QksxxIIIIBApgSC0d9TetRpmaq5HMXG95/r5l81lmOriuzB/KuO2Zl/VZHbkU0RQAABBBBIrYAWWRvoAXtL3eNLU1tkRgqjgZXQQdnGEROLpnhdQsuxDAIIIJAZgfDoOW7+1ajM1FueQpl/VR7nlO2y8kVVnHN4yoqiHAQQQAABBBCopEAQFM4L6hZ9v5I1+LI3DawETzJqrP6DMfboBJdkKQQQQCDdAkEfN//qMeZfbXpKK5ap4i0fSvfZdaM6zfyrDvWYf9WNm4pLEUAAAQQQ8FJAni8MGLWXTJizxst4ZQ5FAytBcDtr973jYvi4sYZJxgm6shQCCKRXQO90jAo+/Ov0Flihyph/VSH4Cm8bP3CeMq0zKlwF2yOAAAIIIIBAWgQKYY+PS+2Cm9JST9broIGV8Am2NVVfqmL7xYSXZTkEEEAglQLB6Olu/tXpqaytkkUx/6qS+pXbm/lXlbNnZwQQQAABBNImoEXfGU5d8pG01ZXlemhgJXx69pZ9t4lWrFpgrd0+4aVZDgEEEEidQPjRm5Vsv2/q6qpsQcy/qqx/hXZn/lWF4NkWAQQQQACB9Am45lVb0KPPaJn01HPpqy67FdHAKsHZ2ebdP1WM9G9KsDRLIoAAAukR0L1VYUr7/Kuq9NSUhkpWPu8GeR+ZhkpKUoPefYoKDr+wJGtneVHmX2X59KgdAQQQQACBZAUCHfwkqF/89WRXZTUaWCW4B6xVOmqonmuV/UAJlmdJBBBAIBUCeqej3fyry1JRS5qKMC03qfiv/52mkhKtJRh7sdJ7TEx0TR8WY/6VD6dIBgQQQAABBLovICKvhYVBe8jkR1d0fzVWeK8ADawS3Q/2ulH7xetXP2qsDUu0BcsigAACFRUIRn/Xzb86o6I1pHHzeO63lVnakMbSEqmpcMJflBpQnchaPi3C/CufTpMsCCCAAAIIdF1A6/DUsL5lZtdX4MrNCdDAKuG90dZY83Nl4i+VcAuWRgABBComEH70Jjf/ar+K7Z/WjX1uZEiPHVQ4cZ5SotPKX5m6mH9VGXd2RQABBBBAIGUC7umruWF96+EiyqasNC/KoYFVwmO0VxzaL+r1yrPWmF1LuA1LI4AAAuUXYP5Vx+YrX3Dzr44o/3mUaUfmX3UMbRbPUfGDvIC4TLch2yCAAAIIIJBKAS0DcEaTAAAgAElEQVQSBUFhrNQufCKVBXpQFA2sEh+im4V1ivsa4awSb8PyCCCAQFkF9E4fdfOveFfFpui+D/Jm/lXHv8ziB76rTOvVZf01yGYIIIAAAgggkC4B0eGPCvUt56SrKr+qoYFVhvNc3zBsjlhzQhm2YgsEEECgLALBfucpve9/lmWvLG0Sz/2Om3/l78gD5l91fDdG149Xdu3SLN2q1IoAAggggAACSQqILCvE/faR055cneSyrLWxAA2sMtwRdvYBQ+K1b803SvUpw3ZsgQACCJRcIPzIjUp2GF3yfbK2AfOvsnZiCdT7zkuqePNhCSzEEggggAACCCCQVQFt9QnhqUtuzWr9WambBlaZTipuHHFubIoXlGk7tkEAAQRKJ6B7qsIU99V+XVW6PbK4MvOvsnhq3a6Z+VfdJmQBBBBAAAEEsi7QVDVtaX3WQ2ShfhpYZTolO396VfTYVY+5VxHsXaYt2QYBBBAoiYDe8SMqOOrykqyd5UWZf5Xl0+t67cy/6rodVyKAAAIIIJB1Aa31ikAKe0vdgpezniUL9dPAKuMp2RnVh8eBvccYhXsZ3dkKAQSSFWD+VceezL9K9j7LymrMv8rKSVEnAggggAACyQsUbPAZOXUxbzZKnrbDFWmklAn6/7Zpa9j998rq08q8LdshgAACiQkw/6pjSuZfJXaLZWch5l9l56yoFAEEEEAAgYQFRMtfw7rWcSLKjbvmpxwCNLDKofyePeydB28bvfr6c1bZ7cq8NdshgAAC3Rdon3812c2/Cph/tRHmyhdVcc7h3fdN6Qp68GQVHPHjlFZXubLM4ltU/OAXKlcAOyOAAAIIIIBARQS0SBQEhbFSu9D9xpifcgnQwCqX9Hv2iZqGfcLE5ooKbM2WCCCAQLcEmH/VMZ//869+ovQek7p17/h4cTxvujJLfu9jNDIhgAACCCCAwPsIuEbKhYVpS78FUnkFaGCV1/vd3aKZ1XcYZT9aoe3ZFgEEEOiSQLDfuUrv+8kuXevzRfED5ynTOsPbiIXj/6zUwGHe5utqMOZfdVWO6xBAAAEEEMiwgMiyQtxvHzntydUZTpHJ0mlgVejY7OwDhsTr337aGNu3QiWwLQIIINB5AQmVHnSQUjscomT7/ZXeyf1zoXfn1/HsCuZfeXagWxOH+Vdbo8RnEEAAAQQQ8E5AW31CeOqSW70LloFANLAqeEhxw4gvxrZ4aQVLYGsEEECgewKbNLRkx7FKqvp0b82sXe15I4P5Vx3fkMy/ytovVOpFAAEEEEAgAQEtDVX1rdMSWIkluiBAA6sLaEldYq3S7quE91ixhyW1JusggAACFRXIYUPLLJ7jBnl/saLspdw8GMv8q458mX9VyruOtRFAAAEEEEifgIi8Hm4T7CPHtbyevuryURENrAqfs51VvUdcVI8ba3tWuBS2RwABBJIXyEFDK37gu27+1dXJ26VkReZfdXwQzL9KyQ1KGQgggAACCJRJoKALk6R+0ewybcc2HQjQwErBbRHPGHJOLPKDFJRCCQgggEBpBTxsaPncyJAeO6hw4jylRJf2vsja6p5/bTRrx0G9CCCAAAIIlF5AZlVNa51S+n3Y4f0EaGCl4P5wXyUMTVP1g7GxY1JQDiUggAAC5RPIekPL80aGHnyKCo64qHz3Q0Z2Yv5VRg6KMhFAAAEEEEhAQJS8EWo1SupbX0tgOZbohgANrG7gJXmpbR45Oo6jh401hSTXZS0EEEAgUwL/1tAa44bCp/dlrb43MoKxFym9xymZuoXKUSzzr8qhzB4IIIAAAgikQ8AGqrZH3dJr0lFNvquggZWi8y82Dv+hNdG3UlQSpSCAAAKVFUh5Q8v3RkZ4/J1KBtZU9h5I4e7RjUcpu3pxCiujJAQQQAABBBBIUsBqPadH/ZITk1yTtbouQAOr63aJX2kXHdsj+uuzf7NK7Z344iyIAAII+CCQsoYW8698uKk6l8GuellFNx3auYv4NAIIIIAAAghkTsB9dfDtsKowSiYvfClzxXtaMA2slB2sbR7+gdhE9xujgpSVRjkIIIBA+gRcQ0sG7Ktk+0OU7PwBJTuW8SuHzL9K3/1QhorMkltVPO/zZdiJLRBAAAEEEECgkgKFIDxN6lpmVLIG9t5YgAZWCu+ItsaanysTfymFpVESAgggkG6BMja0mH+V7luhVNXF876nzJKrSrU86yKAAAIIIIBACgS0treF9cuOT0EplPAeARpYKbwd7F1n9IxeufsRa9Q+KSyPkhBAAIHsCPxbQ+tANxS+XyL1M/8qEcbMLcL8q8wdGQUjgAACCCDQKQGtZGUQ6FFSt/iFTl3Ih0suQAOr5MRd28DO3uvAeN36B3krYdf8uAoBBBDoUCDBhlZ0w4eVXbPES2jpsYMKJ85TSrSX+boaivlXXZXjOgQQQAABBLIjoIPwk2FdyxXZqTg/ldLASvFZFxuqz7fWfifFJVIaAgggkG2BLja0fG9k6MGnqOCIi7J9tiWonvlXJUBlSQQQQAABBFIk4J6++mMwtfUYEeXercZP2gRoYKXtRN5Tj7UqjBpqHrAqPijFZVIaAggg4I+ABG4o/H7vGQrf8VcOfW9kBGMvUnqPU/w514SSMP8qIUiWQQABBBBAIIUCG946GOj9+OpgCg/nnyXRwErv2WyozDaP2iuOVz9qrO2V8lIpDwEEEPBPYDMNLd8bGeFxf1KyzXD/zrObiZh/1U1ALkcAAQQQQCDFAjZQtT3qll6T4hJzXxoNrAzcAu6rhF91XyX8SQZKpUQEEEDAbwHX0NLbjFV21RJl2173MqtUba/CSQ8y/2rT0131iireNM7LMycUAggggAACCIQzq6a1nIpDugVoYKX7fDZU575KqKPG4X+xNjoyA+VSIgIIIIBAhgVk8CQVHsH/ZrLpEZolt6l43lkZPllKRwABBBBAAIGOBETrl8L+ffaVE556C6F0C9DASvf5vFudvXlIdbxKP2GMTeb97xnJTZkIIIAAAuUVYP5Vx97xvPOVWfK78h4GuyGAAAIIIIBASQXc+5aNrQo+Upi8+C8l3YjFExGggZUIY3kWsc27f6oY6d+UZzd2QQABBBDIowDzrzo+deZf5fFXA5kRQAABBHwXCMReEkxddrbvOX3JRwMrYycZzRx2i1Hm+IyVTbkIIIAAAlkQqNpWFSY9xPyrTc+K+VdZuHupEQEEEEAAgU4JuGbIM+GwncfKuHlrO3UhH66YAA2sitF3bWM7+/Cdo3UvPuWGum/btRW4CgEEEEAAgY4FmH/VsQvzr/gVgwACCCCAgF8CWnTR6MK4qroFj/iVzO80NLAyeL62qXpSMbbXZrB0SkYAAQQQSLEA8686PhzmX6X4pqU0BBBAAAEEuiAQaHVOUL/0R124lEsqKEADq4L43dk6aqj+rbH2zO6swbUIIIAAAgi8V4D5Vx3fD8y/4tcJAggggAAC/giIyNywvvVIERX7kyofSWhgZfSc7dX79YmClY9Yq/bMaATKRgABBBBIk0BhkCqc8jDzrzY5E7v6FRXdOC5NJ0UtCCCAAAIIINBFAffWwdVBv2B/+djili4uwWUVFKCBVUH87m5tZ+91YLxu/TxjTVV31+J6BBBAAIF8C8jgiSo84uJ8I3SQ3iy5XcXzPocLAggggAACCHggoAN9Zli35EoPouQyAg2sjB97cebIs61q408cGT9HykcAAQQqLRCM+bHSe06udBmp2z9+8HxlFv8udXVREAIIIIAAAgh0TkCLXBtObeU3O51jS9WnaWCl6jg6X4z7CqHEDcPmGGWO7/zVXIEAAggggMA/BMLj/qhkmxFwbCIQ3fhRZVcvwgUBBBBAAAEEMiygJVwSFAYcKJMfXZHhGLkvnQaWB7eAvaNmh+jN+Alr1E4exCECAggggEC5BZh/1aE486/KfSOyHwIIIIAAAskLuCevIlvocURh8nPzkl+dFcspQAOrnNol3MvOqjkmjuLbjFGcaQmdWRoBBBDwUUD61Ch94LlKdhitpOcgHyN2KRPzr7rExkUIIIAAAgikSkCsfL1wautPUlUUxXRJgGZHl9jSeVFbU/WlKrZfTGd1VIUAAgggkAUB6bunkh0/qGT7A5TsdIiSPjtkoeyS1Mj8q5KwsigCCCCAAAJlE9Ba7gjqWo8TUaZsm7JRyQRoYJWMtvwL20XH9jAPPfdgbO3+5d+dHRFAAAEEfBTIc0OL+Vc+3tFkQgABBBDIi4CI/D0caEfL8UtfzUtm33PSwPLshO2s3feOi/phY1Vvz6IRBwEEEEAgBQIbN7QOdk9o7ZiCqpIvwa5+VUU3fjD5hVkRAQQQQAABBEouoJUyQVWvY2Tys38q+WZsUDYBGlhloy7fRnZGzaeLEl9Wvh3ZCQEEEEAgrwK+NrSYf5XXO5rcCCCAAAI+CLhGx4WFaUu/5UMWMvxLgAaWp3dD28zhM5SKpnkaj1gIIIAAAikV8KWhFT/4fWUWX5lSZcpCAAEEEEAAgc0JiAoeDg849TDZZ3obSn4J0MDy6zzfTWPv2qdv9Mrqh6xRe3kakVgIIIAAAhkQkF5D3NsNxynZ2Q2G3yk7Xzlk/lUGbi5KRAABBBBAYBMBrfWKoG98gJy4rBUc/wRoYPl3pv9qYjUPHhXHwV+Zh+XxIRMNAQQQyJhAFhpazL/K2E1FuQgggAACCPxTQNtCfXjqoiZA/BSggeXnub6bKmoafqaJo996HpN4CCCAAAIZFdi4oXWQGwq/U8WTMP+q4kdAAQgggAACCHRaQAfmsrDu+c92+kIuyIwADazMHFXXC21rrL5KGXt611fgSgQQQAABBMojkIaGFvOvynPW7IIAAggggEBSAhvmXh0y8nAZcfv6pNZknfQJ0MBK35kkXpF94IO9iktenaeUHZ344iyIAAIIIIBACQU2bmiNdU9o7VzC3f6xNPOvSk7MBggggAACCCQmEIi8pfuZMcy9Sow0tQvRwErt0SRbmL1+9Mh43cpHjLH9kl2Z1RBAAAEEECifwHsbWnq7/ZQaMCTRzZl/lSgniyGAAAIIIFBSAa2UUaE9IaxddntJN2LxVAjQwErFMZSniPXNNbUSxQy0Kw83uyCAAAIIlEFAeg12bzk8bMNbDpNoaJklf1DxPMZnlOHo2AIBBBBAAIFuC4iE3ytMbZne7YVYIBMCNLAycUzJFRk11PzG2PhTya3ISggggAACCKRHoLsNrfjBC5RZfEV6AlEJAggggAACCHQooJX+czB1ydEiKoYoHwI0sPJxzu+mtHed0dO8fPfc2KoDcxaduAgggAACORTobEMruuloZVctzKEUkRFAAAEEEMiQgNYvFAbqMXJcy+sZqppSuylAA6ubgFm83N5UMzxebd08LDMgi/VTMwIIIIAAAl0VeL+Gll39dzfA/ZCuLs11CCCAAAIIIFAGAS26aMPC+MKUBXPLsB1bpEiABlaKDqOcpdiGESfHUrzOGMU9UE549kIAAQQQSJWA9K5WstORSnY8SKm1b6r48fNSVR/FIIAAAggggMDGAoVAPi91rb/CJX8CNC/yd+bvJi42Df+BjaNzckxAdAQQQAABBBBAAAEEEEAAgawIaN1cVb+kLivlUmeyAjSwkvXM1GrWKh03DZljjByXqcIpFgEEEEAAAQQQQAABBBBAIFcComVBuHaXg+TMue/kKjhh3xWggZXzm8Hesu828YrVDxtranJOQXwEEEAAAQQQQAABBBBAAIEUCmgt7wRVfQ+WSU89l8LyKKlMAjSwygSd5m3szD33jdW6eUapPmmuk9oQQAABBBBAAAEEEEAAAQTyJaC1ssrKlHBq67X5Sk7aTQVoYHFPbBBgqDs3AgIIIIAAAggggAACCCCAQNoEglB9N6hden7a6qKe8gvQwCq/eWp3jJtG/CSOi19NbYEUhgACCCCAAAIIIIAAAgggkBsBUXKTe/LqZBHlvjDET94FaGDl/Q54T3431D2IZw69xYg6BhYEEEAAAQQQQAABBBBAAAEEKiUgWj0bDlz5ATlu+cpK1cC+6RKggZWu86h4NfYP+wyK31zvhrpHwypeDAUggAACCCCAAAIIIIAAAgjkTiAQeUv31QfLxxa35C48gTcrQAOLm+PfBGzzyNFx3PaAsao3PAgggAACCCCAAAIIIIAAAgiUS8ANbY+VthPC2mW3l2tP9smGAA2sbJxT2auMmodMNZHMLPvGbIgAAggggAACCCCAAAIIIJBbgSDQXwnqlvwstwAE36wADSxujs0KtDXW/FyZ+EsQIYAAAggggAACCCCAAAIIIFB6gXBG1bSW00q/DztkUYAGVhZPrUw1tw91b2scdqNYc0KZtmQbBBBAAAEEEEAAAQQQQACBHAq4uVeP6f6jDpMJc9bkMD6Rt0KABtZWIOX5I/aKQ/tFPV59wKp4VJ4dyI4AAggggAACCCCAAAIIIFAaARF5LewZjJWJLS+WZgdW9UGABpYPp1jiDPbmIdXRO/qv1trtS7wVyyOAAAIIIIAAAggggAACCORIQIsuBmFwlExZdG+OYhO1CwI0sLqAlsdL7KzdjoyLVX801lTlMT+ZEUAAAQQQQAABBBBAAAEEkhfQWn8irF/yu+RXZkXfBGhg+XaiJcwTNYw4w9gi/8VSQmOWRgABBBBAAAEEEEAAAQTyIiA6/FGhvuWcvOQlZ/cEaGB1zy93V8cNQ34aW/lK7oITGAEEEEAAAQQQQAABBBBAIDEBrcPZQV3LZBFlEluUhbwWoIHl9fEmH869mVC3Nbk3ExozIfnVWREBBBBAAAEEEEAAAQQQQMB3gUDLo7rfqCN446DvJ51sPhpYyXrmYrUNbybs+cpca82+uQhMSAQQQAABBBBAAAEEEEAAgWQERJYVRB0i9a2vJbMgq+RFgAZWXk464Zy2af+hkVnR/mbCHRJemuUQQAABBBBAAAEEEEAAAQQ8FNBa3gmq+hwmk55+0sN4RCqxAA2sEgP7vHzxmj0Olaj4Z/dmwh4+5yQbAggggAACCCCAAAIIIIBA9wS0VrHSPT8e1j53S/dW4uq8CtDAyuvJJ5TbXjPy9Dhu+50xinspIVOWQQABBBBAAAEEEEAAAQR8EyhI+DmZ2vJr33KRp3wCNB3KZ+3tTsWG4dOtjb7rbUCCIYAAAggggAACCCCAAAIIdFkgEPuzYOoy3mbfZUEubBeggcV90G0B92ZCMU3DroiN+c9uL8YCCCCAAAIIIIAAAggggAAC3ghobW8L6padKOK+QsgPAt0QoIHVDTwu/ZeAfeRThXjhn+YYY4/GBQEEEEAAAQQQQAABBBBAAIFA5DG9S+8jZPz8VWgg0F0BGljdFeT6dwXsbYP6F5cPuFcpOxoWBBBAAAEEEEAAAQQQQACBPAvI84WqwjiZvPClPCuQPTkBGljJWbKSE7BNe+xSjNvmuX/aHRAEEEAAAQQQQAABBBBAAIH8CWitVwRV7smrSU8/mb/0JC6VAA2sUsnmeF07a/e9TTG4P7Z2mxwzEB0BBBBAAAEEEEAAAQQQyJ2AFlkXFAofcU9e3Z+78AQuqQANrJLy5ndxO2u3I+Ni1R3Gmh75VSA5AggggAACCCCAAAIIIJAfAa2UCXRhstQvmp2f1CQtlwANrHJJ53Cf9c01tYGJG43hbZc5PH4iI4AAAggggAACCCCAQM4ECjr4otQv/kXOYhO3TAI0sMoEnddt4oYR345t8ft5zU9uBBBAAAEEEEAAAQQQQCAPAhLYCwp1y76Th6xkrIwADazKuOdq12jmsF8ZZT6Xq9CERQABBBBAAAEEEEAAAQTyIqCloVDXeqqIsnmJTM7yC9DAKr957na0VoVxw7AbXRPr+NyFJzACCCCAAAIIIIAAAggg4LGAluD2YI8Pf0zGXl70OCbRUiBAAysFh5CHEuwDH+wVLXn1Dqvs4XnIS0YEEEAAAQQQQAABBBBAwHcB0fJIuHPv8TJ+/irfs5Kv8gI0sCp/BrmpwM4aM8AUl98VW3tAbkITFAEEEEAAAQQQQAABBBDwUECLXhyIPVTqW1/zMB6RUihAAyuFh+JzSfaOmh2iN+191piRPuckGwIIIIAAAggggAACCCDgq4CIvB726n+YnPzEQl8zkit9AjSw0ncm3ldkbxheE62N7rdG7eR9WAIigAACCCCAAAIIIIAAAh4JaK1XBFXxeJm07DGPYhElAwI0sDJwSD6WaGfuua+R9fe4rxNu42M+MiGAAAIIIIAAAggggAACvglokbVBGB4jUxbd61s28qRfgAZW+s/I2wpt8/APxFF0p1Gqj7chCYYAAggggAACCCCAAAIIeCDgZl61KdXjpHDqs7d5EIcIGRSggZXBQ/Op5Kh5zxNUvP4GY23oUy6yIIAAAggggAACCCCAAAK+CGit4lj1qO9Rv2CWL5nIkT0BGljZOzPvKo6ah0xVkVztnsTS3oUjEAIIIIAAAggggAACCCCQYQHXvLJKh58Ka1t+m+EYlO6BAA0sDw7Rhwi2qfqsYmx/6UMWMiCAAAIIIIAAAggggAACvgiIqvpqYdrCn/qShxzZFaCBld2z867yYtPwH9g4Ose7YARCAAEEEEAAAQQQQAABBDIoEASF84K6Rd/PYOmU7KEADSwPDzWrkaxVEjcM/4VR0VlZzUDdCCCAAAIIIIAAAggggIAPAoHYS4Kpy872IQsZ/BCggeXHOXqTYkMTq2n3XxujP+1NKIIggAACCCCAAAIIIIAAAlkS0PL7Ql3rf4q4+Vf8IJASARpYKTkIyviXgGti6WLD8N8rFU3DBQEEEEAAAQQQQAABBBBAoHwCWoLrg/rFU1zzKirfruyEwJYFaGBt2YhPVEDANbHCuHlIk4llUgW2Z0sEEEAAAQQQQAABBBBAIHcCWulbgwNOO1n2md6Wu/AETr0ADazUH1F+C7SPfKrQtvBP14mxJ+ZXgeQIIIAAAggggAACCCCAQOkFtJY/BTsfeaKMv2pd6XdjBwQ6L0ADq/NmXFFGATt/elX82NXXG2WOL+O2bIUAAggggAACCCCAAAII5EZAi74zqN7xRBk3b21uQhM0cwI0sDJ3ZPkr2D7wwV7x0tduNcaMz196EiOAAAIIIIAAAggggAACpRMQJfeFu/Y+TsbPX1W6XVgZge4L0MDqviErlEHAzpnQO1r59O3W2iPKsB1bIIAAAggggAACCCCAAALeC4jI3HCX3sfQvPL+qL0ISAPLi2PMRwh726D+0fJt7rQqPigfiUmJAAIIIIAAAggggAACCJRGQAL9QLhm52PkzLnvlGYHVkUgWQEaWMl6slqJBewNHxpo1iz9c2zVgSXeiuURQAABBBBAAAEEEEAAAS8FRMu8cO0uR9O88vJ4vQ1FA8vbo/U3mL2jZofozfgv1qh9/E1JMgQQQAABBBBAAAEEEEAgeQHXvPprOHDFR+W45SuTX50VESidAA2s0tmycgkF7G3Dty++ae5UYvYr4TYsjQACCCCAAAIIIIAAAgh4IxCIPKa37X2UHDN/uTehCJIbARpYuTlq/4LaW/bdJlq56o/W2LH+pSMRAggggAACCCCAAAIIIJCcgGtePe6aVx+meZWcKSuVV4AGVnm92S1hAXvnwdua117/U2ztAQkvzXIIIIAAAggggAACCCCAgBcCNK+8OMbch6CBlftbIPsA7YPdozVL7rBWH5z9NCRAAAEEEEAAAQQQQAABBJITCET9Tfff9miZ8Ogbya3KSgiUX4AGVvnN2bEEAnbWmAFR2/I/WGU/UILlWRIBBBBAAAEEEEAAAQQQyJyAqODhcLuex/C1wcwdHQV3IEADi9vCG4ENTaxo+e1uJtYHvQlFEAQQQAABBBBAAAEEEECgCwKi5L5w/S7Hy5lz3+nC5VyCQOoEaGCl7kgoqDsC9ur9+sThiluMkQ91Zx2uRQABBBBAAAEEEEAAAQSyKqC1vTvYue8EGT9/VVYzUDcCmwrQwOKe8E7gH02sVXOMMeO9C0cgBBBAAAEEEEAAAQQQQOB9BFzz6rZg5/ETZfxV64BCwCcBGlg+nSZZ3hWwcyb0jlfMv9ko82FYEEAAAQQQQAABBBBAAIE8CFit51QdtMcpMuL29XnIS8Z8CdDAytd55yotTaxcHTdhEUAAAQQQQAABBBDIt4CoxkL90tNFVJRvCNL7KkADy9eTJdcGAbvo2B7xQwsbjY1PhgQBBBBAAAEEEEAAAQQQ8FJAS0OhrvUMmldeni6h/ilAA4tbwXsBa1Vgmof9No7NGd6HJSACCCCAAAIIIIAAAgjkSkBr85ug7vnPueaVyVVwwuZOgAZW7o48n4FdE0uKTTU/Uyb+Uj4FSI0AAggggAACCCCAAAK+CWgV/iqY2vIF17yyvmUjDwKbCtDA4p7IlUCxafg3bBxdmKvQhEUAAQQQQAABBBBAAAHvBAKrfxycuuSb3gUjEAKbEaCBxa2RO4H2JpbY6EfGKO7/3J0+gRFAAAEEEEAAAQQQyLaA1sqK6LODuiU/y3YSqkegcwL8Ab5zXnzaEwE7c8RnYlX8lfuSuPYkEjEQQAABBBBAAAEEEEDAcwEtui0O5PQetYubPY9KPAT+TYAGFjdFbgWiGSPqlI5/b6wp5BaB4AgggAACCCCAAAIIIJAJAS1qjQrspLB22e2ZKJgiEUhYgAZWwqAsly2BqHnPE1S8fpaxtle2KqdaBBBAAAEEEEAAAQQQyItAIPKWCasmFKYsmJuXzOREYFMBGljcE7kXsLN2OzJuK9xslO2fewwAEEAAAQQQQAABBBBAIFUCotWroa46RmoXPpGqwigGgTIL0MAqMzjbpVPANu19SGTW3mqt3TadFVIVAggggAACCCCAAAII5E3ADWtfFPaLj5YTl7XmLTt5EdhUgAYW9wQC/xSwNwyvideYP7iZWMNBQQABBBBAAAEEEEAAAQQqKSAqeDoMwqOlbsHLlayDvRFIiwANrLScBDZvyVQAACAASURBVHWkQsDeOnQn87bcFlt7QCoKoggEEEAAAQQQQAABBBDInYCI3BsWBp0okx9dkbvwBEZgMwI0sLg1ENhEwN42qH/81sAb3JNY/wEOAggggAACCCCAAAIIIFBOAavl5qqhO9XKuHlry7kveyGQdgEaWGk/IeqriICdP72q+PhVv1NW1VekADZFAAEEEEAAAQQQQACB3AlokSuC+tbPiKgod+EJjMAWBGhgcYsgsBkBa5WYmcN+FIv5BkgIIIAAAggggAACCCCAQKkEtFbufVLh+YWpLdNLtQfrIpB1ARpYWT9B6i+5QNww9EuumXWJUUqXfDM2QAABBBBAAAEEEEAAgVwJaNFtKog/EdYua8hVcMIi0EkBGlidBOPj+RSwjSMmxjaaaaztmU8BUiOAAAIIIIAAAggggEDSAoHIW0b3OblQ9/TdSa/Negj4JkADy7cTJU/JBIoNNeNF7A3GmAEl24SFEUAAAQQQQAABBBBAIBcCovVLoQ6Pl9qFT+QiMCER6KYADaxuAnJ5vgRs8+BRUVS43SqzW76SkxYBBBBAAAEEEEAAAQQSE7D6yUJvfbxMbHkxsTVZCAHPBWhgeX7AxEtewDbtPzSK377VKrV38quzIgIIIIAAAggggAACCPgsoCW4Pdil52QZP3+VzznJhkDSAjSwkhZlvVwI2CsO7Rf3erHZGDkuF4EJiQACCCCAAAIIIIAAAt0WCLT+nR551Kdl7OXFbi/GAgjkTIAGVs4OnLjJCbg3E4Zxw/CfGxWdldyqrIQAAggggAACCCCAAAK+CWitrLXh+YWpLdN9y0YeBMolQAOrXNLs461AcebIs0W1XWSU0t6GJBgCCCCAAAIIIIAAAgh0SUCLrFOB+WRYu6yhSwtwEQIIbBCggcWNgEACAlHzkGOV0dcYY/slsBxLIIAAAggggAACCCCAgAcCouSNsBBOlCmL7vUgDhEQqKgADayK8rO5TwL2ulH7FdvW3KKMGexTLrIggAACCCCAAAIIIIBA5wVE9FOh7n+i1D2+tPNXcwUCCGwqQAOLewKBBAVs0x67GNt2c2zsmASXZSkEEEAAAQQQQAABBBDIkMCGNw1u81atHLd8ZYbKplQEUi1AAyvVx0NxWRSwV+/Xpy18p1GMPTGL9VMzAggggAACCCCAAAIIdEMgkP8p1LZ+WUS5Mbn8IIBAUgI0sJKSZB0E3iPg3lAYmJnDfhCL+QYwCCCAAAIIIIAAAggg4L+AFt2mVPDpcOqiq/xPS0IEyi9AA6v85uyYIwHbPOxzcWwvNe6duTmKTVQEEEAAAQQQQAABBHIlICKvh4XCyTJ54f25Ck5YBMooQAOrjNhslU8BO6P68Eir66y1O+RTgNQIIIAAAggggAACCPgrIFovDHv2myAnP7HQ35QkQ6DyAjSwKn8GVJADAdtUM9hYcwPD3XNw2EREAAEEEEAAAQQQyI2A1nJHEA6aIpMfXZGb0ARFoEICNLAqBM+2+RNoH+4eF1ZcZWKZlL/0JEYAAQQQQAABBBBAwC+BQOwlun7Z192w9tivZKRBIJ0CNLDSeS5U5amAG+4uUfPwr0sc/dC9kkR7GpNYCCCAAAIIIIAAAgh4K6BF1ikVfpZh7d4eMcFSKkADK6UHQ1l+C0TNQ461kW60yg70OynpEEAAAQQQQAABBBDwSEDrF5QUTq6qW/CIR6mIgkAmBGhgZeKYKNJHAXv96JHRuhU3WqP28jEfmRBAAAEEEEAAAQQQ8ElAa3t3sG04RY5e/HefcpEFgawI0MDKyklRp5cC9rZB/dveHjBDjD3Ry4CEQgABBBBAAAEEEEAg4wJaK2tEflGobT3bzbuKMh6H8hHIrAANrMweHYX7IrBhLlbj8O+KROcZo/g16cvBkgMBBBBAAAEEEEAg8wLuLYOrlFWfCKe2Xpv5MARAIOMC/GE54wdI+f4IRDNH1LtW1m+Ntb38SUUSBBBAAAEEEEAAAQSyKaBFtwRB8SSpfeHpbCagagT8EqCB5dd5kibjArZ51F6RWTWbuVgZP0jKRwABBBBAAAEEEMi0gJbg9mBA76lywlNvZToIxSPgkQANLI8Okyh+CLTPxYpX9LvCxDLJj0SkQAABBBBAAAEEEEAgGwLt864k1hfpaUvOcfOuTDaqpkoE8iFAAysf50zKjAlsmIvVNOJrYqMfuK8Uhhkrn3IRQAABBBBAAAEEEMicgNZ6hQrCM8IpC2/MXPEUjEAOBGhg5eCQiZhdAXvNiCOiYtRsld05uymoHAEEEEAAAQQQQACBdAsEIo/pvnqyfGxxS7orpToE8itAAyu/Z0/yjAjYO2p2iF+3jUaZD2ekZMpEAAEEEEAAAQQQQCBDAuGMwoC9PiMT5qzJUNGUikDuBGhg5e7ICZxFAfeVwjBqHP5tkeg8YxS/brN4iNSMAAIIIIAAAgggkCoBrWWVMuGnw2mLGlNVGMUggECHAvxBmBsDgQwJRE3VJ9pY/d59pXBghsqmVAQQQAABBBBAAAEEUiXgBrQ/FwbxKVL7wtOpKoxiEEBgswI0sLg5EMiYgL1u/xHFtSuvU2L2y1jplIsAAggggAACCCCAQAoE3FcGTe/PymlPrk5BMZSAAAJbKUADayuh+BgCaRKwcyb0Lq584tfK6tPSVBe1IIAAAggggAACCCCQVgEtslZp+XxYt+TKtNZIXQggsHkBGljcHQhkWMA21JwWi/mVMbZvhmNQOgIIIIAAAggggAACJRUQrReGVb1PkUlPP1nSjVgcAQRKJkADq2S0LIxAeQTs9aNHmrUrm2NrDyjPjuyCAAIIIIAAAggggEB2BLRWNwS9hn5CTrr77exUTaUIILCpAA0s7gkEPBCwi47tUXz4uZ9oZT/PWwo9OFAiIIAAAggggAACCHRboP0rg4HRX5ZTF/+m24uxAAIIVFyABlbFj4ACEEhOYMNbCo260lq7bXKrshICCCCAAAIIIIAAAtkSEK3mh1V96/nKYLbOjWoReD8BGljcHwh4JmBnD98tWhc3uCbWEZ5FIw4CCCCAAAIIIIAAAu8r4L4uaJUN/jfov/eXZcKcNXAhgIA/AjSw/DlLkiDwroC1Kogah39HJPq2+0phAA0CCCCAAAIIIIAAAr4LiMjrEvT4RFj73C2+ZyUfAnkUoIGVx1Mnc24E7KzdjoyiqgZrzK65CU1QBBBAAAEEEEAAgdwJaNF3BrpwutQteDl34QmMQE4EaGDl5KCJmV8BO2fMdm3vvHWlGDMhvwokRwABBBBAAAEEEPBRwDWuilbpH4b1LeeLKONjRjIhgMA/BGhgcScgkAMB95VCMY0jvmBV9GNjbc8cRCYiAggggAACCCCAgOcCrmH1XNjDukHtyx7zPCrxEECABhb3AAL5ErDNo/YyZvWM2Ngx+UpOWgQQQAABBBBAAAG/BMIZBdP7s3Lak6v9ykUaBBDYnABPYHFvIJAzATt/elX0xMzpoqKvM+A9Z4dPXAQQQAABBBBAIOMCblD7m6GEn5b6RbMzHoXyEUCgkwI0sDoJxscR8EXANg//QBSbq601I3zJRA4EEEAAAQQQQAABfwW0ljuCsHCmTF74kr8pSYYAApsToIHFvYFAjgXsnAm9i6ue/pGK7RdzzEB0BBBAAAEEEEAAgRQLaFFr3B9cz9H1S//Hzb2yKS6V0hBAoIQCNLBKiMvSCGRFwM6qOSZqM1daZXfOSs3UiQACCCCAAAIIIOC/gGiZF1YNOF0mPb7I/7QkRACB9xOggcX9gQACGwTsbcO3j5bH/+uaWB+DBAEEEEAAAQQQQACBSgpokXVWB9PD2paL3VNXcSVrYW8EEEiHAA2sdJwDVSCQGgHbUHNaLOaXxth+qSmKQhBAAAEEEEAAAQRyIyCin7KFXqdVTZ7/eG5CExQBBLYoQANri0R8AIH8CdjZI4dF64puwLs9NH/pSYwAAggggAACCCBQCQH31FVktbkwHHH0+TL28mIlamBPBBBIrwANrPSeDZUhUFEBa5Woa3b/r9gEP3VPY/WtaDFsjgACCCCAAAIIIOC1gJZwSVDQp7s3DN7vdVDCIYBAlwVoYHWZjgsRyIeAvalmeLTKXOGexjoiH4lJiQACCCCAAAIIIFAuAa2UMYH8sjBkp2/KuHlry7Uv+yCAQPYEaGBl78yoGIGyC7znaayLmY1Vdn42RAABBBBAAAEEvBTQohe7h/7/qzB18V1eBiQUAggkKkADK1FOFkPAbwHbtP/Q2Kz8X2PNUX4nJR0CCCCAAAIIIIBAqQTaZ10Zrf5fodjvHDntydWl2od1EUDALwEaWH6dJ2kQKLnAhqexGmtONcr8PLZ2m5JvyAYIIIAAAggggAAC3ghseMNgoM6sql3ysDehCIIAAmURoIFVFmY2QcA/ATv78J2jtS/+2ir7Mf/SkQgBBBBAAAEEEEAgSQH3dcGiGHWJPvC082Sf6W1Jrs1aCCCQDwEaWPk4Z1IiUDKBqKH6FKvUr92Q921LtgkLI4AAAggggAACCGRWQLTMC8P4kzL5+WcyG4LCEUCg4gI0sCp+BBSAQPYFbNMeu7TZ4mVizITspyEBAggggAACCCCAQBIC7g2Dq0XUubp+6S/c300Sa7IGAgjkV4AGVn7PnuQIJC4QXVM9wcTyK/cy5MGJL86CCCCAAAIIIIAAApkRECX3hb37f1JOfmJhZoqmUAQQSLUADaxUHw/FIZA9ATtrzIC47a0fKmU+4/5nNvc/vPGDAAIIIIAAAgggkBeBQOQtq4OvBbUtV7qnrtykCX4QQACBZARoYCXjyCoIILCJgJ2914HR+nW/scaOBQcBBBBAAAEEEEDAfwEtcm2wnf68HL347/6nJSECCJRbgAZWucXZD4EcCVirQtM49CwrcoExtm+OohMVAQQQQAABBBDIjYB7w2BLEOizpLblj7kJTVAEECi7AA2sspOzIQL5E7CzRu4aF+P/MTY+OX/pSYwAAggggAACCPgp4J64WmdV8OPw4BE/khG3r/czJakQQCAtAjSw0nIS1IFADgQY8p6DQyYiAggggAACCORCQGt9V1DV53My6annchGYkAggUHEBGlgVPwIKQCBfAu1D3ovx8vO1tWcZo4J8pSctAggggAACCCCQbQHR6tXQBt+QqYuvznYSqkcAgawJ0MDK2olRLwKeCNimvQ8xZu1lsbX7exKJGAgggAACCCCAgLcCWqtYmfCyoGrAuTL50RXeBiUYAgikVoAGVmqPhsIQ8F/ADXnXqrFmWqTMxdba7f1PTEIEEEAAAQQQQCB7AoHI41r3+ozUPfPX7FVPxQgg4IsADSxfTpIcCGRYwN558Lbx31/9gTL6v4xyTS1+EEAAAQQQQAABBCouICLLQ63OU7Wtl4m4J7D4QQABBCooQAOrgvhsjQACGwvY64YcEK3Xv3BPYx2KDQIIIIAAAggggEBlBNz/muhGlYYNhUHqbDmu5fXKVMGuCCCAwMYCNLC4IxBAIFUC7muFEjdWTzIiP3W/dRqcquIoBgEEEEAAAQQQ8FxAJLwnDPSXpHbhE55HJR4CCGRMgAZWxg6MchHIi4C9er8+UbDma6LMN401PfKSm5wIIIAAAggggEAlBETrl0Ir56j6xTPc1wVtJWpgTwQQQOD9BGhgcX8ggECqBez1o0fGa1f93Nj42FQXSnEIIIAAAggggEAGBbTIWjHyP3q3XhfI+PmrMhiBkhFAICcCNLByctDERCDrAtE11RNU0V7qhrxXZz0L9SOAAAIIIIAAAmkQsKJvqeoXf1FOXNaahnqoAQEEEHg/ARpY3B8IIJAZAfvAB3uZ1jfOthJ9wxjbNzOFUygCCCCAAAIIIJAiAdFqvpKqLxXqFv45RWVRCgIIIPC+AjSwuEEQQCBzAvbWoTvFbwffc+Pez3SvyAkyF4CCEUAAAQQQQACBCgiIkjdC0d9zc64uc3OuogqUwJYIIIBAlwVoYHWZjgsRQKDSAva6ffeM168631h7SqVrYX8EEEAAAQQQQCCtAlp0m9H2skLPId+Vk+5+O611UhcCCCDwfgI0sLg/EEAg8wK2YdhRRWsvVsqOznwYAiCAAAIIIIAAAgkJaO3eJmjluqBn4ZsyceGShJZlGQQQQKAiAjSwKsLOpgggkLSAtUqrxpppkTUXut+p7Zz0+qyHAAIIIIAAAghkSUC0zFOx/mrh1MUPZKluakUAAQQ2J0ADi3sDAQS8ErBzJvSOVj37BbHxuW7Qez+vwhEGAQQQQAABBBDYgoBrXC0Qq74TTm29FiwEEEDAJwEaWD6dJlkQQOBdAdu0xy6xib7LoHduCgQQQAABBBDIg0D7gHYt9gJdv/RXDGjPw4mTEYH8CdDAyt+ZkxiBXAm0zdpnfx2tudA9jXV0roITFgEEEEAAAQRyIaBFrbHaXhKu2e0iOXPuO7kITUgEEMilAA2sXB47oRHIn4CdtduRUVz1QxubcflLT2IEEEAAAQQQ8E1Ai0Qu01VBoTBdJi98ybd85EEAAQQ2FaCBxT2BAAK5Emh/Y6FR5sexVQfmKjhhEUAAAQQQQMALgXffLNir/7fl5CcWehGKEAgggMBWCNDA2gokPoIAAn4JuDcWStxYPcmKXGCNGelXOtIggAACCCCAgK8CWvSdQc8e35CJz/7N14zkQgABBDYnQAOLewMBBHIr4BpZoWqsqY9tPN0oVZ1bCIIjgAACCCCAQKoFRGRuWCieK5NfvCfVhVIcAgggUEIBGlglxGVpBBDIhoCdP71KPXXlGVEcnG+t3TEbVVMlAggggAACCPguIGIekjC4IJzSOsf3rORDAAEEtiRAA2tLQvznCCCQGwF71z59o1fXnyXWfMsYMyA3wQmKAAIIIIAAAqkScH9Ie8Y9dTU9qG+9TkTZVBVHMQgggECFBGhgVQiebRFAIL0Cds6Y7cw7b3/dKvNZY2zf9FZKZQgggAACCCDgk4CbcdWignh6MGVZk2tcuQkH/CCAAAII/J8ADSzuBQQQQGAzAu2NrGjFm2eLlrNcI6sfUAgggAACCCCAQCkENjSuRP0wGHnUTBl7ebEUe7AmAgggkHUBGlhZP0HqRwCBkgvYOw/eNnpt+ReUjb9klR1Y8g3ZAAEEEEAAAQRyIaCVag1Cc6Ga8vyV7omrKBehCYkAAgh0UYAGVhfhuAwBBPInYK84tF/U+7XPaRN/I7Z2m/wJkBgBBBBAAAEEkhBon3EVSvBjVb+4kcZVEqKsgQACeRCggZWHUyYjAggkKvB/jSxl4q+7txYOSnRxFkMAAQQQQAABbwVEq/mhDS5yjasG17iKvQ1KMAQQQKAEAjSwSoDKkgggkA+B9rcWmpdXn2mUfMs1snbMR2pSIoAAAggggEBnBUT0U6GSi2lcdVaOzyOAAAL/EqCBxd2AAAIIdFOg/Yks0+e1s0wcf8U1srbv5nJcjgACCCCAAAKeCARaHrWizg9qW+e4J66sJ7GIgQACCFREgAZWRdjZFAEEfBSwcyb0Vu88c2Ys8VdMrIb6mJFMCCCAAAIIILBlAbFyvyi5MDx1ya1b/jSfQAABBBDYGgEaWFujxGcQQACBTghYq3Q8q/p4W9TfsSo+qBOX8lEEEEAAAQQQyKiA1u59xVbfqsPChYUpC+ZmNAZlI4AAAqkVoIGV2qOhMAQQ8EHAzhp5WFsx+oZYc4IPeciAAAIIIIAAAhsLaNFtxuprClVtF8rk55/BBwEEEECgNAI0sErjyqoIIIDARgJts/bZX7Wt/4qWuM5YG8KDAAIIIIAAAtkW0FreMaJ+V6gKfiITW17MdhqqRwABBNIvQAMr/WdEhQgg4JGAvXlIdXG1/m9t7CeNVb09ikYUBBBAAAEEciEgIq8pFVwWDuh1qZzw1Fu5CE1IBBBAIAUCNLBScAiUgAAC+ROwtw3fPnpLnaVU/AX35sJB+RMgMQIIIIAAAtkScF8VXCzK/EJX73y5jJu3NlvVUy0CCCCQfQEaWNk/QxIggECGBewVh/YzPf/+n27Y+xeMNcMzHIXSEUAAAQQQ8FLAPXF1bxhUXaKmLJgjroPlZUhCIYAAAhkQoIGVgUOiRAQQ8F+g/c2FqnHYf7Qp9aVAzPHGKP772f9jJyECCCCAQEoF2gezK2VvCoLgEqlteTClZVIWAgggkCsB/oCUq+MmLAIIZEHAXj96ZHH9yrN0bM90/zNvnyzUTI0IIIAAAgj4INA+30obuUr3CH8hkxe+5EMmMiCAAAK+CNDA8uUkyYEAAt4J2FljBpjim2fESr6srB3iXUACIYAAAgggkBKBQOQxHcSXqd13ncF8q5QcCmUggAACmwjQwOKWQAABBFIu0P71wnhW9fEqki+6OVlHpbxcykMAAQQQQCATAlopE4u+rUqpS2XqkjszUTRFIoAAAjkWoIGV48MnOgIIZE/AXjfkgHh9+Bn3e+5TjbW9speAihFAAAEEEKisgFay0gTqqkLVwEtk4mPLKlsNuyOAAAIIbK0ADaytleJzCCCAQIoEbGP1jiY2Z1qRT7o5WdUpKo1SEEAAAQQQSKVAoOVRa+LfBGZgo5z25OpUFklRCCCAAAKbFaCBxc2BAAIIZFjg/95eGCv7KaXk4+4rhoUMx6F0BBBAAAEEEhXQIuvcgnMCJZfzNcFEaVkMAQQQKLsADayyk7MhAgggUBoBO/vwnaO2V04TFX3GxGpoaXZhVQQQQAABBNIvIFoWKAl+F24/6Ldy1ENvpr9iKkQAAQQQ2JIADawtCfGfI4AAAhkT2PipLHWSm5UVZiwC5SKAAAIIINBpAS26TSl7U/vTVqp+yZ9FlO30IlyAAAIIIJBaARpYqT0aCkMAAQS6L2Cb9tglUvGpNo4/535Tv3v3V2QFBBBAAAEE0iXgGlctVuvfhgPUlXJcy+vpqo5qEEAAAQSSEqCBlZQk6yCAAAIpFnBPZYXq2iET4vXyKSXqo27wu3t7OD8IIIAAAghkU2DD01Y6vjkwwW942iqbZ0jVCCCAQGcFaGB1VozPI4AAAhkXsLNG7mqKbZOM0mdaa/bNeBzKRwABBBDIkYD7w8szKgiv5mmrHB06URFAAIF/CtDA4lZAAAEEcizQ1rD7GKWD08SoqdbabXNMQXQEEEAAgZQKBCJvWaWvNap4edXU5x9NaZmUhQACCCBQYgEaWCUGZnkEEEAgCwL2rjN6xi/fMyFWclqg7DEMfs/CqVEjAggg4K+A+567UaL/4gayz1DVO1wr4+at9TctyRBAAAEEtkaABtbWKPEZBBBAIEcC7YPfjVl/Smz0J5SY/XIUnagIIIAAAhUWEC0LlA2aw8KAK2Xyo89XuBy2RwABBBBIkQANrBQdBqUggAACaRMozqgZJ1qdrsROMcYMSFt91IMAAgggkH2BDV8RlHhWoKuuktqWB7OfiAQIIIAAAqUQoIFVClXWRAABBDwTsA98sFfb0uUTArW+Vlk51n3FsKdnEYmDAAIIIFBGAS2y1lg1pxBWNaoxw/4gI25fX8bt2QoBBBBAIIMCNLAyeGiUjAACCFRSwM4aM0AV3/5Ym7KnBEodbawpVLIe9kYAAQQQyIaA1iq2Vh4Mg/hq1X9Vsxy3fGU2KqdKBBBAAIE0CNDASsMpUAMCCCCQUQH7h30GqTfXnbChmSXmWGOU62nxgwACCCCAwL8EAi2PKmtnuKeumqW+9TVsEEAAAQQQ6IoADayuqHENAggggMC/CdhZI3c1xbZJ7rVRp4jYca6Zxf+P4T5BAAEEcirg/h/AM0rCa8Peaoac1LI4pwzERgABBBBIUIA/XCSIyVIIIIAAAv8QsDfVDDerdK2RYq01ah9cEEAAAQT8F9jwBkFlrgtVnyapf2a+/4lJiAACCCBQTgEaWOXUZi8EEEAghwK2efAo983Ck1UsJ7nhJ/vnkIDICCCAgL8CVj8ZFMxsN97qeql94Wl/g5IMAQQQQOD/t3e3zVVVVwDH19rn3BtICIQhoCAqSZGHwiBI6YjQjpaMzqDpaFU60xftu34OP0dnOtNxptLW8sbQThVH6xQ1WiFVBBxI1cQiSCU8JJKHc85eXUlLZ/qiQiTJvefe/51hLlzOPXut376QsNh77VoLUMCq9QwwPgIIINBEAnZox71x4vKTJuEJX6f1sJ9mmDZR+qSKAAIINITAf7cHtiz+jT5z4qOGSIokEEAAAQTqXoACVt1PEQEigAACjSlgfTs75dqV/f85zfBRP82w2piZkhUCCCBQboHg+wJ9e+BAtORw2rLk1/rM386WOyOiRwABBBAoowAFrDLOGjEjgAACDSZgz29rK1pGfxCz5NkQiidjtPYGS5F0EEAAgVIJBN8TaKb9QezFUKn+Xg+cOVeqBAgWAQQQQKDhBChgNdyUkhACCCBQbgF7a/diGR55NItTT6nJE2a2otwZET0CCCBQDgEVvWIqL4ck9iWdd/xJe969VI7IiRIBBBBAoBkEKGA1wyyTIwIIIFBSAfNmWdkL9+zQUO2RWPSq2kMxCl+7SjqfhI0AAvUnEDT92HsSvippPJxs/dnLuuW5qfqLkogQQAABBBAQ/hHAhwABBBBAoDwC9sf1K4vLhTd/T3pVil4T6yhP9ESKAAII1F7gxtZACUlfmkz16YHhU7WPiggQQAABBBC4uQD/i31zI65AAAEEEKhDAV+dlciLG3bHyfwJSazHi1kPsDqrDieKkBBAoOYCqnrJv+l/LZFwWFrvfkmf+vOVmgdFAAgggAACCMxSgALWLMG4HAEEEECgPgXs4PZ1otf2F7nsF42PRJPW+oyUqBBAAIH5FQiquYm8ExJ7JYZFR9JnP3pXfdnq/I7K3RFAAAEEEJhfAQpY8+vL3RFAAAEEaiAw0wj+H1f2xsnJfTGxfSq2w1dnJTUIhSERQACBBREIGgbF5BWppEeS9i9f0/0j1xZkYAZBAAEEEEBggQQoYC0QNMMggAACCNROwF7fskQ+H3/QUe0kNwAABzpJREFUi1g9bDes3TwwMgIIzJ1ACDompv0q8XBot5f0h0OfzN3duRMCCCCAAAL1J0ABq/7mhIgQQAABBOZZwA6tXyt52Jdlscd31ezz/lmr53lIbo8AAgjclsDMtkCTt0OSHglqR+THg39lW+BtkfJmBBBAAIGSCVDAKtmEES4CCCCAwNwL2KEN3ZJN9BRF4gUt6SnMls/9KNwRAQQQuHWB6YKVF6jel0Jftaq9mVTv/QvN12/djysRQAABBBpPgAJW480pGSGAAAII3IbA9OmG2Qv3bPfTuvZasL1mutei3Hkbt+StCCCAwE0FvGA17gdQvGNWeUPE3ki7VvXrQ2+P3/SNXIAAAggggECTCFDAapKJJk0EEEAAgW8uYAc3rini1J4YZG9isse3HD7g/bT4GvrNSXknAk0vEFSum+hAiHo0BHlV1nz/qD7yq4mmhwEAAQQQQACB/yPAN998NBBAAAEEEJilgL38rVVyNd8TM/leFNujkjwQzdJZ3obLEUCgiQRU9LwG6/dVnW+lSXLUe1i951sE8yYiIFUEEEAAAQRuS4AC1m3x8WYEEEAAAQR8s8/z29ryysSDQab2Wi4PWtBdZrYCGwQQaE6BoGHK/w44biG8Y0H6q5Wlb+vTA0PNqUHWCCCAAAIIzI0ABay5ceQuCCCAAAII/I/AzLbDMLXT8mSnH3O/RyzujiJtMCGAQOMJTK+uiqrHgoRjqcSj0nXHm/Svarx5JiMEEEAAgdoKUMCqrT+jI4AAAgg0iYA3h0/ltxu2FBJ3SSy+qya7TGQrWw+b5ANAmg0j4MWqKxp0QDU5Fkz6JdV+PXDmXMMkSCIIIIAAAgjUqQAFrDqdGMJCAAEEEGh8Aevrbc2vn9kRisldhRe0VNVXa9l6bxCfNH72ZIhA/Qv4n8mL3qdqwA8nPZ5qcVyqleP69JmP6z9yIkQAAQQQQKDxBChgNd6ckhECCCCAQIkF7ORzVXn/d/eJTe7MQtzpJ5R92089vN/76awscVqEjkDdC9zYBphGORmCnhJtOaY/OXWy7gMnQAQQQAABBJpEgAJWk0w0aSKAAAIIlFvADm5fJzp+f7S4zWRqm8Rku/fV6va+WqHcmRE9AgsrEFSu+8qq04XIh1rIyTTYB9KZDOhjf7+4sJEwGgIIIIAAAgjMRoAC1my0uBYBBBBAAIE6ErDXtyyRLya3Smb3FyFuFdFNUWWjxHh3HYVJKAjURGD6JEDvMXfazwk9lQQ5EULLKWkpPpQnBz/xbYFe++WBAAIIIIAAAmUSoIBVptkiVgQQQAABBG5BwH65pz1r/XJjsGKTqWz2hvEbTfJNauE+X8FVvYVbcAkCpREIqhNmYVA1ntZgJ4PoSaks+VCePuGvSV6aRAgUAQQQQAABBL5WgAIWHxAEEEAAAQSaRGDmJMRD27uK7Opms3RTUNsYrdjsa1Hu8z5bnU3CQJolFPDVVJOm8eMoejYN4WzIw2Ce+M+rbYPyo4HPWFFVwkklZAQQQAABBGYpQAFrlmBcjgACCCCAQCMKTK/akrZ/dhcqXV7o6tYi6xLTLguxW/3Zt2ItasS8yal+BGZWUqkMaVRfOVWcCUkyKKHlrCy6Pii9Q8NepPK2VTwQQAABBBBAoFkFKGA168yTNwIIIIAAArMQsIMb1+RButJYdEfNvbgVumK0bhO517dsrWZr4iwwm/RSb5x+yU8cGI4mw5Z4oUorQ6llwxIrw9IxNayPf3qhSWlIGwEEEEAAAQRuQYAC1i0gcQkCCCCAAAIIfL2A/WHdnTJuq4sirE2y9K5cbHVI4j1mttq3J66VqHf5cweOjSngxamLXtS8qCGejzG5oIl8luY2XFTy4STYkEx0DOlPP/iqMbMnKwQQQAABBBBYCAEKWAuhzBgIIIAAAgggINbX2yrZ8Np8slijNrHWYrgjhHyV06z0TtudQWWFmHRKtFUUu2r/gfH5uC6SXjApzquaF6j0nIb0oqmdn349ytQXlZB+Lt0PX9Tv/CKrfcREgAACCCCAAAKNLEABq5Fnl9wQQAABBBAoqYC99/OKDJ3ulOzTFbkt64yWrarq1MrozeYLUy902QqTsDSI+Q9pN0mWeaGlw7+xWer9utKSpj0vYYego75tbyQRuWyiIxKKEYnpiJk/m45omowkSTYi4r8OHZdFJ0akunlEe/u8gMUDAQQQQAABBBCoDwEKWPUxD0SBAAIIIIAAAnMkYG/tXiyfXlgqbaE9G8uWaVjUkSaTSyVL232IpXnQtiD5IsnDYqloaoVMv+6PuPzfT9YuIaQWpDWYtUTTqkpoEz/6zhuML5tNmKa+qS7Gmfd4oe0r32o3NTOESKaSjN24l/cnvyo6/bL/XiHjGnRi5vfUozO55kU5HzwZVcnHosq4aGU01ThamEx4vKNJJYyKTI5nWRirLF4+KpNj49LeOiaPnxj15ue+wI0HAggggAACCCBQboF/AahUm6uc7YCFAAAAAElFTkSuQmCC", + "created": 1704443366413, + "lastRetrieved": 1704700745150 + }, + "8ec1dc6981e6ee38b87d2c4cc93ce1a5a287cc35": { + "mimeType": "image/svg+xml", + "id": "8ec1dc6981e6ee38b87d2c4cc93ce1a5a287cc35", + "dataURL": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzVweCIgaGVpZ2h0PSIzNnB4IiB2aWV3Qm94PSIwIDAgMzUgMzYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+bG9nby1mdWxsLWJsYWNrPC90aXRsZT4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJsb2dvLWZ1bGwtYmxhY2siIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMzMzNjIwLCAwLjUyNzgzMCkiPgogICAgICAgICAgICA8cGF0aCBkPSJNMy4zNzcxLDEzLjE2MjM3IEM1LjE5NDU4LDcuNDIxMDYgMTAuNTQ2NzgsMy4yNjE0NyAxNi44NjYzOCwzLjI2MTQ3IEMyNC42ODIwOCwzLjI2MTQ3IDMxLjAxNzk4LDkuNjIzOTcgMzEuMDE3OTgsMTcuNDcyMjcgQzMxLjAxNzk4LDI0Ljc2MzE3IDI1LjU1MDI4LDMwLjc3MTQ3IDE4LjUwNzc4LDMxLjU4ODI3IEwxOC41MDc3OCwyNy4xMTMxNyBDMjEuNTk1MDgsMjYuNTg3OTcgMjQuMTg4MTgsMjQuNjA1NjcgMjUuNTYwMzgsMjEuODk2MDcgTDIxLjYzOTM4LDIxLjg5NjA3IEMyMC40NTI2OCwyMy4xODQ2NyAxOC43NTQxOCwyMy45OTE1NyAxNi44Njc3OCwyMy45OTE1NyBDMTMuMjc5OTgsMjMuOTkxNTcgMTAuMzcxNDgsMjEuMDcyNjcgMTAuMzcxNDgsMTcuNDcyMTcgQzEwLjM3MTQ4LDEzLjg3MTY3IDEzLjI3OTk4LDEwLjk1MzA3IDE2Ljg2Nzc4LDEwLjk1MzA3IEMxOC44MTAxOCwxMC45NTMwNyAyMC41NTM2OCwxMS44MDg2NyAyMS43NDQyOCwxMy4xNjQ4NyBMMjUuNjE4MjgsMTMuMTY0ODcgQzI0LjAzMTA4LDkuOTIzOTcgMjAuNzA5MTgsNy42OTMzNyAxNi44Njc3OCw3LjY5MzM3IEMxMS40ODYwOCw3LjY5MzM3IDcuMTIzNDgsMTIuMDcxNTcgNy4xMjM0OCwxNy40NzIxNyBDNy4xMjM0OCwyMi4zMTgzNyAxMC42MzYyOCwyNi4zNDEyNyAxNS4yNDU5OCwyNy4xMTYxNyBMMTUuMjQ1OTgsMzEuNTkwNjcgQzkuNjg2NzgsMzAuOTU0MzcgNS4xMDU0OCwyNy4wODM1NyAzLjQxNDM5LDIxLjg5ODQ3IEwwLjAyOTg0LDIxLjg5ODQ3IEMxLjk4MTU2LDI5LjQwNDc3IDguNzc5NzgsMzQuOTQ0MjcgMTYuODY2MzgsMzQuOTQ0MjcgQzI2LjQ3NTk4LDM0Ljk0NDI3IDM0LjI2NjE4LDI3LjEyMTk3IDM0LjI2NjE4LDE3LjQ3MjI3IEMzNC4yNjYxOCw3LjgyMjU3IDI2LjQ3NTc4LDAgMTYuODY2MzgsMCBDOC43Mzc4OCwwIDEuOTExMjQsNS41OTcgMCwxMy4xNjIzNyBMMy4zNzcxLDEzLjE2MjM3IFoiIGlkPSJTaGFwZSIgZmlsbD0iIzBBMTQxOCIvPgogICAgICAgICAgICA8cGF0aCBkPSJNMjAuNDI5MjgsMTcuNDY0NzcgQzIwLjQyOTI4LDE5LjQ0NDI3IDE4LjgzMDk4LDIxLjA0ODg3IDE2Ljg1OTQ4LDIxLjA0ODg3IEMxNC44ODgwOCwyMS4wNDg4NyAxMy4yODk3OCwxOS40NDQyNyAxMy4yODk3OCwxNy40NjQ3NyBDMTMuMjg5NzgsMTUuNDg1NDcgMTQuODg4MDgsMTMuODgwODcgMTYuODU5NDgsMTMuODgwODcgQzE4LjgzMDk4LDEzLjg4MDg3IDIwLjQyOTI4LDE1LjQ4NTQ3IDIwLjQyOTI4LDE3LjQ2NDc3IFoiIGlkPSJQYXRoIiBmaWxsPSIjRkY3QzJCIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==", + "created": 1704445033257, + "lastRetrieved": 1704700745150 + } + } +} \ No newline at end of file diff --git a/docs/static/arch.png b/docs/static/arch.png new file mode 100644 index 000000000..307060f47 Binary files /dev/null and b/docs/static/arch.png differ diff --git a/go.mod b/go.mod index 2c78339f4..3c453131d 100644 --- a/go.mod +++ b/go.mod @@ -1,69 +1,86 @@ -go 1.20 +go 1.21 module github.com/babylonchain/babylon require ( - 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 - github.com/cosmos/cosmos-sdk v0.47.3 - github.com/golang/protobuf v1.5.3 - github.com/gorilla/mux v1.8.0 + github.com/CosmWasm/wasmd v0.50.0 + github.com/btcsuite/btcd v0.24.0 + github.com/cometbft/cometbft v0.38.5 + github.com/cometbft/cometbft-db v0.9.1 // indirect + // TODO: the current cosmos-sdk version is a non-released version, which is + // to fix the gasLimit problem + // should be replaced by a released version if there's any + github.com/cosmos/cosmos-sdk v0.50.4-0.20240126152601-c4a2fe2b8987 + github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/pkg/errors v0.9.1 - github.com/rakyll/statik v0.1.7 // indirect - github.com/spf13/cast v1.5.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 - github.com/supranational/blst v0.3.8 - google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 - google.golang.org/grpc v1.55.0 - gopkg.in/yaml.v2 v2.4.0 // indirect + github.com/spf13/cast v1.6.0 + github.com/spf13/cobra v1.8.0 + github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.8.4 + github.com/supranational/blst v0.3.11 + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( - cosmossdk.io/api v0.3.1 - cosmossdk.io/errors v1.0.0-beta.7 - cosmossdk.io/math v1.0.1 - cosmossdk.io/tools/rosetta v0.2.1 - github.com/CosmWasm/wasmvm v1.2.3 + cosmossdk.io/api v0.7.2 + cosmossdk.io/client/v2 v2.0.0-beta.1 + cosmossdk.io/core v0.11.0 + cosmossdk.io/depinject v1.0.0-alpha.4 + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.0 + cosmossdk.io/math v1.2.0 + cosmossdk.io/store v1.0.2 + cosmossdk.io/tools/confix v0.1.0 + cosmossdk.io/x/circuit v0.1.0 + cosmossdk.io/x/evidence v0.1.0 + cosmossdk.io/x/feegrant v0.1.0 + cosmossdk.io/x/tx v0.13.0 + cosmossdk.io/x/upgrade v0.1.0 + github.com/CosmWasm/wasmvm v1.5.2 github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d - github.com/btcsuite/btcd/btcutil v1.1.2 - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 - github.com/cosmos/cosmos-proto v1.0.0-beta.2 - github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-go/v7 v7.0.0 + github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/btcsuite/btcd/btcutil v1.1.5 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 + github.com/cosmos/cosmos-db v1.0.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.3 + github.com/cosmos/gogoproto v1.4.11 + github.com/cosmos/ibc-go/modules/capability v1.0.0 + github.com/cosmos/ibc-go/v8 v8.0.0 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 + github.com/docker/docker v23.0.8+incompatible github.com/golang/mock v1.6.0 + github.com/golang/protobuf v1.5.3 + github.com/hashicorp/go-metrics v0.5.1 github.com/jinzhu/copier v0.3.5 github.com/ory/dockertest/v3 v3.9.1 - google.golang.org/protobuf v1.30.0 + github.com/vulpine-io/io-test v1.0.0 + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f ) require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/keyring v1.2.1 // indirect - github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect - github.com/armon/go-metrics v0.4.1 github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/iavl v0.20.0 // indirect - github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cosmos/iavl v1.0.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/dvsekhvalnov/jose2go v1.5.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -71,147 +88,167 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/orderedcode v0.0.1 // indirect - github.com/gorilla/handlers v1.5.1 // indirect + github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/gtank/merlin v0.1.1 // indirect - github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect - github.com/klauspost/compress v1.16.3 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.0 - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/cors v1.8.3 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/pflag v1.0.5 - github.com/subosito/gotenv v1.4.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/zondax/hid v0.9.1 // indirect - go.etcd.io/bbolt v1.3.7 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + golang.org/x/crypto v0.18.0 + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect nhooyr.io/websocket v1.8.6 // indirect ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.18.0 // indirect + cloud.google.com/go v0.110.10 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.12.0 // indirect - cloud.google.com/go/storage v1.29.0 // indirect - cosmossdk.io/core v0.5.1 // indirect - cosmossdk.io/depinject v1.0.0-alpha.3 // indirect - cosmossdk.io/log v1.1.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/storage v1.35.1 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/x/nft v0.1.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/aws/aws-sdk-go v1.44.203 // indirect + github.com/aead/siphash v1.0.1 // indirect + github.com/aws/aws-sdk-go v1.44.224 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/bits-and-blooms/bitset v1.8.0 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect - github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20231102162011-844f0582c2eb // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect - github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect - github.com/creachadair/taskgroup v0.4.2 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/creachadair/atomicfile v0.3.1 // indirect + github.com/creachadair/tomledit v0.0.24 // indirect github.com/danieljoos/wincred v1.1.2 // indirect - 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.2+incompatible // indirect - github.com/docker/docker v20.10.24+incompatible // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/cli v23.0.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/emicklei/dot v1.6.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/getsentry/sentry-go v0.25.0 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.1 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/linxGnu/grocksdb v1.8.6 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/opencontainers/runc v1.1.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/rs/zerolog v1.29.1 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/zerolog v1.31.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect - github.com/tidwall/btree v1.6.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/zondax/ledger-go v0.14.1 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/tools v0.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.110.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/api v0.153.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - pgregory.net/rapid v0.5.5 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + gotest.tools/v3 v3.5.1 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) +// Long lived replaces of Cosmos SDK +// see: https://github.com/cosmos/cosmos-sdk/blob/v0.50.3/go.mod replace ( - github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 - - // slay the dragonberry - github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 - github.com/cosmos/ibc-go/v7 => github.com/babylonchain/ibc-go/v7 v7.0.0-20230324085744-4d6a0d2c0fcf - + // use cosmos fork of keyring + github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 // Fix upstream GHSA-h395-qcrw-5vmq vulnerability. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 - github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 + github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.1 // Downgraded to stable version see: https://github.com/cosmos/cosmos-sdk/pull/14952 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 diff --git a/go.sum b/go.sum index c7559bb31..474d59744 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -16,7 +15,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -32,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -70,8 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -111,13 +109,12 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -171,12 +168,11 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -188,37 +184,58 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= -cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= -cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= -cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE= -cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= -cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= -cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= -cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= -cosmossdk.io/log v1.1.0 h1:v0ogPHYeTzPcBTcPR1A3j1hkei4pZama8kz8LKlCMv0= -cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= -cosmossdk.io/math v1.0.1 h1:Qx3ifyOPaMLNH/89WeZFH268yCvU4xEcnPLu3sJqPPg= -cosmossdk.io/math v1.0.1/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= -cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= -cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= +cosmossdk.io/api v0.7.2 h1:BO3i5fvKMKvfaUiMkCznxViuBEfyWA/k6w2eAF6q1C4= +cosmossdk.io/api v0.7.2/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= +cosmossdk.io/client/v2 v2.0.0-beta.1/go.mod h1:JEUSu9moNZQ4kU3ir1DKD5eU4bllmAexrGWjmb9k8qU= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= +cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= +cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/tools/confix v0.1.0 h1:2OOZTtQsDT5e7P3FM5xqM0bPfluAxZlAwxqaDmYBE+E= +cosmossdk.io/tools/confix v0.1.0/go.mod h1:TdXKVYs4gEayav5wM+JHT+kTU2J7fozFNqoVaN+8CdY= +cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= +cosmossdk.io/x/circuit v0.1.0/go.mod h1:YDzblVE8+E+urPYQq5kq5foRY/IzhXovSYXb4nwd39w= +cosmossdk.io/x/evidence v0.1.0 h1:J6OEyDl1rbykksdGynzPKG5R/zm6TacwW2fbLTW4nCk= +cosmossdk.io/x/evidence v0.1.0/go.mod h1:hTaiiXsoiJ3InMz1uptgF0BnGqROllAN8mwisOMMsfw= +cosmossdk.io/x/feegrant v0.1.0 h1:c7s3oAq/8/UO0EiN1H5BIjwVntujVTkYs35YPvvrdQk= +cosmossdk.io/x/feegrant v0.1.0/go.mod h1:4r+FsViJRpcZif/yhTn+E0E6OFfg4n0Lx+6cCtnZElU= +cosmossdk.io/x/nft v0.1.0 h1:VhcsFiEK33ODN27kxKLa0r/CeFd8laBfbDBwYqCyYCM= +cosmossdk.io/x/nft v0.1.0/go.mod h1:ec4j4QAO4mJZ+45jeYRnW7awLHby1JZANqe1hNZ4S3g= +cosmossdk.io/x/tx v0.13.0 h1:8lzyOh3zONPpZv2uTcUmsv0WTXy6T1/aCVDCqShmpzU= +cosmossdk.io/x/tx v0.13.0/go.mod h1:CpNQtmoqbXa33/DVxWQNx5Dcnbkv2xGUhL7tYQ5wUsY= +cosmossdk.io/x/upgrade v0.1.0 h1:z1ZZG4UL9ICTNbJDYZ6jOnF9GdEK9wyoEFi4BUScHXE= +cosmossdk.io/x/upgrade v0.1.0/go.mod h1:/6jjNGbiPCNtmA1N+rBtP601sr0g4ZXuj3yC6ClPCGY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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.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/CosmWasm/wasmd v0.50.0 h1:NVaGqCSTRfb9UTDHJwT6nQIWcb6VjlQl88iI+u1+qjE= +github.com/CosmWasm/wasmd v0.50.0/go.mod h1:UjmShW4l9YxaMytwJZ7IB7MWzHiynSZP3DdWrG0FRtk= +github.com/CosmWasm/wasmvm v1.5.2 h1:+pKB1Mz9GZVt1vadxB+EDdD1FOz3dMNjIKq/58/lrag= +github.com/CosmWasm/wasmvm v1.5.2/go.mod h1:Q0bSEtlktzh7W2hhEaifrFp1Erx11ckQZmjq8FLCyys= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -228,9 +245,10 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -242,18 +260,15 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.44.203 h1:pcsP805b9acL3wUqa4JR2vg1k2wnItkDYNvfmcy6F+U= -github.com/aws/aws-sdk-go v1.44.203/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.224 h1:09CiaaF35nRmxrzWZ2uRq5v6Ghg/d2RiPjZnSgtt+RQ= +github.com/aws/aws-sdk-go v1.44.224/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/babylonchain/ibc-go/v7 v7.0.0-20230324085744-4d6a0d2c0fcf h1:NJU3YuruPqV8w6/y45Zsb8FudcWSkTBugdpTT7kJmjw= -github.com/babylonchain/ibc-go/v7 v7.0.0-20230324085744-4d6a0d2c0fcf/go.mod h1:BFh8nKWjr5zeR2OZfhkzdgDzj1+KjRn3aJLpwapStj8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -263,24 +278,27 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd v0.23.4 h1:IzV6qqkfwbItOS/sg/aDfPDsjPP8twrCOE2R93hxMlQ= -github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= +github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= -github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -291,7 +309,11 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -306,6 +328,9 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -331,15 +356,24 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= -github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v0.0.0-20231102162011-844f0582c2eb h1:6Po+YYKT5B5ZXN0wd2rwFBaebM0LufPf8p4zxOd48Kg= +github.com/cockroachdb/pebble v0.0.0-20231102162011-844f0582c2eb/go.mod h1:acMRUGd/BK8AUmQNK3spUCCGzFLZU2bSST3NMXSq2Kc= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= -github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= -github.com/cometbft/cometbft v0.37.1 h1:KLxkQTK2hICXYq21U2hn1W5hOVYUdQgDQ1uB+90xPIg= -github.com/cometbft/cometbft v0.37.1/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs= -github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= -github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= +github.com/cometbft/cometbft v0.38.5 h1:4lOcK5VTPrfbLOhNHmPYe6c7eDXHtBdMCQuKbAfFJdU= +github.com/cometbft/cometbft v0.38.5/go.mod h1:0tqKin+KQs8zDwzYD8rPHzSBIDNPuB4NrwwGDNb/hUg= +github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= +github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= @@ -352,52 +386,56 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= -github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= -github.com/cosmos/cosmos-sdk v0.47.3 h1:r0hGmZoAzP2D+MaPaFGHwAaTdFQq3pNpHaUp1BsffbM= -github.com/cosmos/cosmos-sdk v0.47.3/go.mod h1:c4OfLdAykA9zsj1CqrxBRqXzVz48I++JSvIMPSPcEmk= -github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 h1:iKclrn3YEOwk4jQHT2ulgzuXyxmzmPczUalMwW4XH9k= -github.com/cosmos/cosmos-sdk/ics23/go v0.8.0/go.mod h1:2a4dBq88TUoqoWAU5eu0lGvpFP3wWDPgdHPargtyw30= -github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= +github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= +github.com/cosmos/cosmos-sdk v0.50.4-0.20240126152601-c4a2fe2b8987 h1:Pjvcy7wHUoYh253LvNv5Dyx+d3SNkRPsDZH+FytqZ3w= +github.com/cosmos/cosmos-sdk v0.50.4-0.20240126152601-c4a2fe2b8987/go.mod h1:0D9mrUy1eAUMQuvYzf2xvhEPk2ta9w7XH1zcYvyFiuM= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= -github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= -github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= -github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab h1:I9ialKTQo7248V827Bba4OuKPmk+FPzmTVHsLXaIJWw= -github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab/go.mod h1:2CwqasX5dSD7Hbp/9b6lhK6BwoBDCBldx7gPKRukR60= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= -github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= -github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g= -github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= -github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/iavl v1.0.0 h1:bw6t0Mv/mVCJvlMTOPHWLs5uUE3BRBfVWCRelOzl+so= +github.com/cosmos/iavl v1.0.0/go.mod h1:CmTGqMnRnucjxbjduneZXT+0vPgNElYvdefjX2q9tYc= +github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= +github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= +github.com/cosmos/ibc-go/v8 v8.0.0 h1:QKipnr/NGwc+9L7NZipURvmSIu+nw9jOIWTJuDBqOhg= +github.com/cosmos/ibc-go/v8 v8.0.0/go.mod h1:C6IiJom0F3cIQCD5fKwVPDrDK9j/xTu563AWuOmXois= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= +github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= -github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creachadair/atomicfile v0.3.1 h1:yQORkHjSYySh/tv5th1dkKcn02NEW5JleB84sjt+W4Q= +github.com/creachadair/atomicfile v0.3.1/go.mod h1:mwfrkRxFKwpNAflYZzytbSwxvbK6fdGRRlp0KEQc0qU= +github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6QKQDhQ= +github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0= -github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= @@ -410,12 +448,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/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.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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v23.0.8+incompatible h1:z4ZCIwfqHgOEwhxmAWugSL1PFtPQmLP60EVhJYJPaX8= +github.com/docker/docker v23.0.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -425,13 +463,14 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= -github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.0 h1:vUzuoVE8ipzS7QkES4UfxdpCwdU2U97m2Pb2tQCoYRY= +github.com/emicklei/dot v1.6.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -443,27 +482,34 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -472,6 +518,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -479,27 +526,31 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= @@ -511,8 +562,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -571,8 +622,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -583,6 +635,7 @@ github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -594,24 +647,26 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -621,37 +676,32 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= -github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= -github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= -github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -661,11 +711,17 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/Hqck/U= +github.com/hashicorp/go-metrics v0.5.1/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= @@ -673,22 +729,25 @@ github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoD github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -697,6 +756,8 @@ github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0Jr github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= @@ -704,13 +765,13 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -729,42 +790,49 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.8.6 h1:O7I6SIGPrypf3f/gmrrLUBQDKfO8uOoYdWf4gLS06tc= +github.com/linxGnu/grocksdb v1.8.6/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -772,6 +840,7 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -780,17 +849,15 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= -github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -807,14 +874,15 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20221120202655-abb19827d345 h1:J9c53/kxIH+2nTKBEfZYFMlhghtHpIHSXpm5VRGHSnU= -github.com/moby/term v0.0.0-20221120202655-abb19827d345/go.mod h1:15ce4BGCFxt7I5NQKT+HV0yEDxmf6fSysfEDiVo3zFM= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= @@ -832,19 +900,26 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -863,6 +938,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= @@ -871,22 +947,26 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= -github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -894,52 +974,55 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= -github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= -github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -953,39 +1036,40 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -996,30 +1080,31 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/supranational/blst v0.3.8 h1:glwLF4oBRSJOTr05lRBgNwGQST0ndP2wg29fSeTRKCY= -github.com/supranational/blst v0.3.8/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= -github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1027,6 +1112,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= +github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1042,13 +1129,13 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= -github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= -github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1065,11 +1152,20 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1079,14 +1175,13 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1098,8 +1193,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1126,8 +1221,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1189,8 +1285,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1216,8 +1315,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1232,7 +1331,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1253,7 +1354,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1293,13 +1393,11 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1331,21 +1429,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1355,13 +1459,19 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1383,6 +1493,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1416,7 +1527,6 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1424,8 +1534,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1484,8 +1595,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4= +google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1493,8 +1604,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1533,10 +1645,8 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1603,8 +1713,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1646,8 +1760,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1664,14 +1778,16 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -1698,8 +1814,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1710,12 +1826,13 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= -pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/privval/file.go b/privval/file.go index 05183c218..deebd10da 100644 --- a/privval/file.go +++ b/privval/file.go @@ -6,16 +6,15 @@ import ( "os" "path/filepath" - "github.com/cosmos/cosmos-sdk/crypto/codec" - - tmcrypto "github.com/cometbft/cometbft/crypto" + cmtcrypto "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/ed25519" - tmjson "github.com/cometbft/cometbft/libs/json" - tmos "github.com/cometbft/cometbft/libs/os" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/libs/tempfile" "github.com/cometbft/cometbft/privval" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/babylonchain/babylon/crypto/bls12381" @@ -35,11 +34,11 @@ const ( // copied from github.com/cometbft/cometbft/privval/file.go" // //nolint:unused -func voteToStep(vote *tmproto.Vote) int8 { +func voteToStep(vote *cmtproto.Vote) int8 { switch vote.Type { - case tmproto.PrevoteType: + case cmtproto.PrevoteType: return stepPrevote - case tmproto.PrecommitType: + case cmtproto.PrecommitType: return stepPrecommit default: panic(fmt.Sprintf("Unknown vote type: %v", vote.Type)) @@ -50,8 +49,8 @@ func voteToStep(vote *tmproto.Vote) int8 { type WrappedFilePVKey struct { DelegatorAddress string `json:"acc_address"` Address types.Address `json:"address"` - PubKey tmcrypto.PubKey `json:"pub_key"` - PrivKey tmcrypto.PrivKey `json:"priv_key"` + PubKey cmtcrypto.PubKey `json:"pub_key"` + PrivKey cmtcrypto.PrivKey `json:"priv_key"` BlsPubKey bls12381.PublicKey `json:"bls_pub_key"` BlsPrivKey bls12381.PrivateKey `json:"bls_priv_key"` @@ -65,7 +64,7 @@ func (pvKey WrappedFilePVKey) Save() { panic("cannot save PrivValidator key: filePath not set") } - jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ") + jsonBytes, err := cmtjson.MarshalIndent(pvKey, "", " ") if err != nil { panic(err) } @@ -75,7 +74,7 @@ func (pvKey WrappedFilePVKey) Save() { } } -//------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- // WrappedFilePV wraps FilePV with WrappedFilePVKey. type WrappedFilePV struct { @@ -84,7 +83,7 @@ type WrappedFilePV struct { } // NewWrappedFilePV wraps FilePV -func NewWrappedFilePV(privKey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKey, keyFilePath, stateFilePath string) *WrappedFilePV { +func NewWrappedFilePV(privKey cmtcrypto.PrivKey, blsPrivKey bls12381.PrivateKey, keyFilePath, stateFilePath string) *WrappedFilePV { filePV := privval.NewFilePV(privKey, keyFilePath, stateFilePath) return &WrappedFilePV{ Key: WrappedFilePVKey{ @@ -122,12 +121,12 @@ func LoadWrappedFilePVEmptyState(keyFilePath, stateFilePath string) *WrappedFile func loadWrappedFilePV(keyFilePath, stateFilePath string, loadState bool) *WrappedFilePV { keyJSONBytes, err := os.ReadFile(keyFilePath) if err != nil { - tmos.Exit(err.Error()) + cmtos.Exit(err.Error()) } pvKey := WrappedFilePVKey{} - err = tmjson.Unmarshal(keyJSONBytes, &pvKey) + err = cmtjson.Unmarshal(keyJSONBytes, &pvKey) if err != nil { - tmos.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) + cmtos.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) } // overwrite pubkey and address for convenience @@ -141,16 +140,16 @@ func loadWrappedFilePV(keyFilePath, stateFilePath string, loadState bool) *Wrapp if loadState { stateJSONBytes, err := os.ReadFile(stateFilePath) if err != nil { - tmos.Exit(err.Error()) + cmtos.Exit(err.Error()) } - err = tmjson.Unmarshal(stateJSONBytes, &pvState) + err = cmtjson.Unmarshal(stateJSONBytes, &pvState) if err != nil { - tmos.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) + cmtos.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) } } // adding path is not needed - //pvState.filePath = stateFilePath + // pvState.filePath = stateFilePath return &WrappedFilePV{ Key: pvKey, @@ -162,7 +161,7 @@ func loadWrappedFilePV(keyFilePath, stateFilePath string, loadState bool) *Wrapp // or else generates a new one and saves it to the filePaths. func LoadOrGenWrappedFilePV(keyFilePath, stateFilePath string) *WrappedFilePV { var pv *WrappedFilePV - if tmos.FileExists(keyFilePath) { + if cmtos.FileExists(keyFilePath) { pv = LoadWrappedFilePV(keyFilePath, stateFilePath) } else { pv = GenWrappedFilePV(keyFilePath, stateFilePath) @@ -173,7 +172,7 @@ func LoadOrGenWrappedFilePV(keyFilePath, stateFilePath string) *WrappedFilePV { // ExportGenBls writes a {address, bls_pub_key, pop, and pub_key} into a json file func (pv *WrappedFilePV) ExportGenBls(filePath string) (outputFileName string, err error) { - if !tmos.FileExists(filePath) { + if !cmtos.FileExists(filePath) { return outputFileName, errors.New("export file path does not exist") } @@ -187,7 +186,7 @@ func (pv *WrappedFilePV) ExportGenBls(filePath string) (outputFileName string, e return outputFileName, err } - pubkey, err := codec.FromTmPubKeyInterface(validatorKey.ValPubkey) + pubkey, err := codec.FromCmtPubKeyInterface(validatorKey.ValPubkey) if err != nil { return outputFileName, err } @@ -197,7 +196,7 @@ func (pv *WrappedFilePV) ExportGenBls(filePath string) (outputFileName string, e return outputFileName, err } - jsonBytes, err := tmjson.MarshalIndent(genbls, "", " ") + jsonBytes, err := cmtjson.MarshalIndent(genbls, "", " ") if err != nil { return outputFileName, err } @@ -215,7 +214,7 @@ func (pv *WrappedFilePV) GetAddress() sdk.ValAddress { } addr, err := sdk.AccAddressFromBech32(pv.Key.DelegatorAddress) if err != nil { - tmos.Exit(err.Error()) + cmtos.Exit(err.Error()) } return sdk.ValAddress(addr) } @@ -227,11 +226,11 @@ func (pv *WrappedFilePV) SetAccAddress(addr sdk.AccAddress) { // GetPubKey returns the public key of the validator. // Implements PrivValidator. -func (pv *WrappedFilePV) GetPubKey() (tmcrypto.PubKey, error) { +func (pv *WrappedFilePV) GetPubKey() (cmtcrypto.PubKey, error) { return pv.Key.PubKey, nil } -func (pv *WrappedFilePV) GetValPrivKey() tmcrypto.PrivKey { +func (pv *WrappedFilePV) GetValPrivKey() cmtcrypto.PrivKey { return pv.Key.PrivKey } @@ -255,6 +254,10 @@ func (pv *WrappedFilePV) GetBlsPubkey() (bls12381.PublicKey, error) { return blsPrivKey.PubKey(), nil } +func (pv *WrappedFilePV) GetValidatorPubkey() (cmtcrypto.PubKey, error) { + return pv.GetPubKey() +} + // Save persists the FilePV to disk. func (pv *WrappedFilePV) Save() { pv.Key.Save() diff --git a/privval/types.go b/privval/types.go index ceb5334ef..17ed68fcf 100644 --- a/privval/types.go +++ b/privval/types.go @@ -3,22 +3,22 @@ package privval import ( "errors" - tmcrypto "github.com/cometbft/cometbft/crypto" + cmtcrypto "github.com/cometbft/cometbft/crypto" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/x/checkpointing/types" ) type ValidatorKeys struct { - ValPubkey tmcrypto.PubKey + ValPubkey cmtcrypto.PubKey BlsPubkey bls12381.PublicKey PoP *types.ProofOfPossession - valPrivkey tmcrypto.PrivKey + valPrivkey cmtcrypto.PrivKey blsPrivkey bls12381.PrivateKey } -func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKey) (*ValidatorKeys, error) { +func NewValidatorKeys(valPrivkey cmtcrypto.PrivKey, blsPrivKey bls12381.PrivateKey) (*ValidatorKeys, error) { pop, err := BuildPoP(valPrivkey, blsPrivKey) if err != nil { return nil, err @@ -34,7 +34,7 @@ func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKe // BuildPoP builds a proof-of-possession by PoP=sign(key = BLS_sk, data = sign(key = Ed25519_sk, data = BLS_pk)) // where valPrivKey is Ed25519_sk and blsPrivkey is BLS_sk -func BuildPoP(valPrivKey tmcrypto.PrivKey, blsPrivkey bls12381.PrivateKey) (*types.ProofOfPossession, error) { +func BuildPoP(valPrivKey cmtcrypto.PrivKey, blsPrivkey bls12381.PrivateKey) (*types.ProofOfPossession, error) { if valPrivKey == nil { return nil, errors.New("validator private key is empty") } diff --git a/proto/babylon/btccheckpoint/v1/btccheckpoint.proto b/proto/babylon/btccheckpoint/v1/btccheckpoint.proto index 68126cc7b..9dae49c47 100644 --- a/proto/babylon/btccheckpoint/v1/btccheckpoint.proto +++ b/proto/babylon/btccheckpoint/v1/btccheckpoint.proto @@ -71,7 +71,7 @@ enum BtcStatus { EPOCH_STATUS_FINALIZED = 2 [ (gogoproto.enumvalue_customname) = "Finalized" ]; } -// TransactionInfo is the info of a tx that contains Babylon checkpoint, +// TransactionInfo is the info of a tx on Bitcoin, // including // - the position of the tx on BTC blockchain // - the full tx content @@ -112,11 +112,11 @@ message SubmissionData { // TODO: Add btc blockheight at epooch end, when adding hadnling of epoching // callbacks message EpochData { - // List of all received checkpoints during this epoch, sorted by order of - // submission. - repeated SubmissionKey key = 1; + // keys is the list of all received checkpoints during this epoch, sorted by + // order of submission. + repeated SubmissionKey keys = 1; - // Current btc status of the epoch + // status is the current btc status of the epoch BtcStatus status = 2; } diff --git a/proto/babylon/btccheckpoint/v1/tx.proto b/proto/babylon/btccheckpoint/v1/tx.proto index d0d12b363..38fb5a173 100644 --- a/proto/babylon/btccheckpoint/v1/tx.proto +++ b/proto/babylon/btccheckpoint/v1/tx.proto @@ -11,6 +11,8 @@ option go_package = "github.com/babylonchain/babylon/x/btccheckpoint/types"; // Msg defines the Msg service. service Msg { + option (cosmos.msg.v1.service) = true; + // InsertBTCSpvProof tries to insert a new checkpoint into the store. rpc InsertBTCSpvProof(MsgInsertBTCSpvProof) returns (MsgInsertBTCSpvProofResponse); @@ -22,6 +24,8 @@ service Msg { // MsgInsertBTCSpvProof defines resquest to insert a new checkpoint into the // store message MsgInsertBTCSpvProof { + option (cosmos.msg.v1.signer) = "submitter"; + string submitter = 1; repeated babylon.btccheckpoint.v1.BTCSpvProof proofs = 2; } diff --git a/proto/babylon/btclightclient/v1/btclightclient.proto b/proto/babylon/btclightclient/v1/btclightclient.proto index 6c1c400fb..f5300706c 100644 --- a/proto/babylon/btclightclient/v1/btclightclient.proto +++ b/proto/babylon/btclightclient/v1/btclightclient.proto @@ -22,5 +22,5 @@ message BTCHeaderInfo { "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" ]; uint64 height = 3; bytes work = 4 - [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint" ]; + [ (gogoproto.customtype) = "cosmossdk.io/math.Uint" ]; } diff --git a/proto/babylon/btclightclient/v1/genesis.proto b/proto/babylon/btclightclient/v1/genesis.proto index 4b15f0714..4de7fce3d 100644 --- a/proto/babylon/btclightclient/v1/genesis.proto +++ b/proto/babylon/btclightclient/v1/genesis.proto @@ -3,10 +3,12 @@ package babylon.btclightclient.v1; import "gogoproto/gogo.proto"; import "babylon/btclightclient/v1/btclightclient.proto"; +import "babylon/btclightclient/v1/params.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; // GenesisState defines the btclightclient module's genesis state. message GenesisState { - BTCHeaderInfo base_btc_header = 1 [ (gogoproto.nullable) = false ]; + Params params = 1 [(gogoproto.nullable) = false]; + BTCHeaderInfo base_btc_header = 2 [ (gogoproto.nullable) = false ]; } diff --git a/proto/babylon/btclightclient/v1/params.proto b/proto/babylon/btclightclient/v1/params.proto new file mode 100644 index 000000000..ea0f0c709 --- /dev/null +++ b/proto/babylon/btclightclient/v1/params.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package babylon.btclightclient.v1; + +import "gogoproto/gogo.proto"; + + +option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; + +// Params defines the parameters for the module. +message Params { + option (gogoproto.equal) = true; + + // List of addresses which are allowed to insert headers to btc light client + // if the list is empty, any address can insert headers + repeated string insert_headers_allow_list = 1; +} diff --git a/proto/babylon/btclightclient/v1/query.proto b/proto/babylon/btclightclient/v1/query.proto index a562cfece..146cbf601 100644 --- a/proto/babylon/btclightclient/v1/query.proto +++ b/proto/babylon/btclightclient/v1/query.proto @@ -5,11 +5,17 @@ import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; import "babylon/btclightclient/v1/btclightclient.proto"; +import "babylon/btclightclient/v1/params.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/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/btclightclient/v1/params"; + } + // Hashes retrieves the hashes maintained by the module. rpc Hashes(QueryHashesRequest) returns (QueryHashesResponse) { option (google.api.http).get = "/babylon/btclightclient/v1/hashes"; @@ -44,6 +50,21 @@ service Query { rpc BaseHeader(QueryBaseHeaderRequest) returns (QueryBaseHeaderResponse) { option (google.api.http).get = "/babylon/btclightclient/v1/baseheader"; } + + // HeaderDepth returns the depth of the header in main chain or error if the + // block is not found or it exists on fork + rpc HeaderDepth(QueryHeaderDepthRequest) returns(QueryHeaderDepthResponse) { + option (google.api.http).get = "/babylon/btclightclient/v1/depth/{hash}"; + } +} + +// 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.btclightclient.v1.Params params = 1 [ (gogoproto.nullable) = false ]; } // QueryHashesRequest is request type for the Query/Hashes RPC method. @@ -105,3 +126,11 @@ message QueryBaseHeaderRequest {} // QueryBaseHeaderResponse is the response type for the Query/BaseHeader RPC // method. message QueryBaseHeaderResponse { BTCHeaderInfo header = 1; } + +// QueryMainChainDepthRequest is the request type for the Query/MainChainDepth RPC +// it contains hex encoded hash of btc block header as parameter +message QueryHeaderDepthRequest { string hash = 1; } + +// QueryMainChainDepthResponse is the response type for the Query/MainChainDepth RPC +// it contains depth of the block in main chain +message QueryHeaderDepthResponse { uint64 depth = 1; } diff --git a/proto/babylon/btclightclient/v1/tx.proto b/proto/babylon/btclightclient/v1/tx.proto index 30127aaee..1c884ba87 100644 --- a/proto/babylon/btclightclient/v1/tx.proto +++ b/proto/babylon/btclightclient/v1/tx.proto @@ -2,23 +2,50 @@ syntax = "proto3"; package babylon.btclightclient.v1; import "gogoproto/gogo.proto"; +import "cosmos/msg/v1/msg.proto"; +import "babylon/btclightclient/v1/params.proto"; +import "cosmos_proto/cosmos.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; // Msg defines the Msg service. service Msg { - // InsertHeader adds a header to the BTC light client chain maintained by - // Babylon. - rpc InsertHeader(MsgInsertHeader) returns (MsgInsertHeaderResponse) {}; + option (cosmos.msg.v1.service) = true; + + // InsertHeaders adds a batch of headers to the BTC light client chain + rpc InsertHeaders(MsgInsertHeaders) returns (MsgInsertHeadersResponse) {}; + + // UpdateParams defines a method for updating btc light client module parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); } -// MsgInsertHeader defines the message for incoming header bytes -message MsgInsertHeader { +// MsgInsertHeaders defines the message for multiple incoming header bytes +message MsgInsertHeaders { + option (cosmos.msg.v1.signer) = "signer"; + string signer = 1; - bytes header = 2 + repeated bytes headers = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderBytes" ]; } +// MsgInsertHeadersResponse defines the response for the InsertHeaders transaction +message MsgInsertHeadersResponse {} + +// MsgUpdateParams defines a message for updating btc light client 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 btc light client parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} -// MsgInsertHeaderResponse defines the response for the InsertHeader transaction -message MsgInsertHeaderResponse {} +// MsgUpdateParamsResponse is the response to the MsgUpdateParams message. +message MsgUpdateParamsResponse {} diff --git a/proto/babylon/btcstaking/v1/btcstaking.proto b/proto/babylon/btcstaking/v1/btcstaking.proto new file mode 100644 index 000000000..89b284156 --- /dev/null +++ b/proto/babylon/btcstaking/v1/btcstaking.proto @@ -0,0 +1,208 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/crypto/secp256k1/keys.proto"; +import "cosmos/staking/v1beta1/staking.proto"; +import "babylon/btcstaking/v1/pop.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// FinalityProvider defines a finality provider +message FinalityProvider { + // description defines the description terms for the finality provider. + cosmos.staking.v1beta1.Description description = 1; + // commission defines the commission rate of the finality provider. + string commission = 2 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; + // babylon_pk is the Babylon secp256k1 PK of this finality provider + cosmos.crypto.secp256k1.PubKey babylon_pk = 3; + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 5; + // slashed_babylon_height indicates the Babylon height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_babylon_height = 6; + // slashed_btc_height indicates the BTC height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_btc_height = 7; +} + +// FinalityProviderWithMeta wraps the FinalityProvider with metadata. +message FinalityProviderWithMeta { + // btc_pk is the Bitcoin secp256k1 PK of thisfinality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // height is the queried Babylon height + uint64 height = 2; + // voting_power is the voting power of this finality provider at the given height + uint64 voting_power = 3; + // slashed_babylon_height indicates the Babylon height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_babylon_height = 4; + // slashed_btc_height indicates the BTC height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_btc_height = 5; +} + +// BTCDelegation defines a BTC delegation +message BTCDelegation { + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + cosmos.crypto.secp256k1.PubKey babylon_pk = 1; + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 3; + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + repeated bytes fp_btc_pk_list = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // start_height is the start BTC height of the BTC delegation + // it is the start BTC height of the timelock + uint64 start_height = 5; + // end_height is the end height of the BTC delegation + // it is the end BTC height of the timelock - w + uint64 end_height = 6; + // total_sat is the total amount of BTC stakes in this delegation + // quantified in satoshi + uint64 total_sat = 7; + // staking_tx is the staking tx + bytes staking_tx = 8; + // staking_output_idx is the index of the staking output in the staking tx + uint32 staking_output_idx = 9; + // slashing_tx is the slashing tx + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + bytes slashing_tx = 10 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + bytes delegator_sig = 11 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // covenant_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_sigs = 12; + // unbonding_time describes how long the funds will be locked either in unbonding output + // or slashing change output + uint32 unbonding_time = 13; + // btc_undelegation is the information about the early unbonding path of the BTC delegation + BTCUndelegation btc_undelegation = 14; +} + +// BTCUndelegation contains the information about the early unbonding path of the BTC delegation +message BTCUndelegation { + // unbonding_tx is the transaction which will transfer the funds from staking + // output to unbonding output. Unbonding output will usually have lower timelock + // than staking output. + bytes unbonding_tx = 1; + // slashing_tx is the slashing tx for unbonding transactions + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + bytes slashing_tx = 2 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_unbonding_sig is the signature on the unbonding tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It effectively proves that the delegator wants to unbond and thus + // Babylon will consider this BTC delegation unbonded. Delegator's BTC + // on Bitcoin will be unbonded after timelock + bytes delegator_unbonding_sig = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // delegator_slashing_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the unbonding tx output. + bytes delegator_slashing_sig = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // covenant_slashing_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_slashing_sigs = 5; + // covenant_unbonding_sig_list is the list of signatures on the unbonding tx + // by covenant members + // It must be provided after processing undelagate message by Babylon + repeated SignatureInfo covenant_unbonding_sig_list = 6; +} + + +// BTCUndelegationInfo provides all necessary info about the undeleagation +message BTCUndelegationInfo { + // unbonding_tx is the transaction which will transfer the funds from staking + // output to unbonding output. Unbonding output will usually have lower timelock + // than staking output. + bytes unbonding_tx = 1; + // covenant_unbonding_sig_list is the list of signatures on the unbonding tx + // by covenant members + repeated SignatureInfo covenant_unbonding_sig_list = 2; + // covenant_slashing_sigs is a list of adaptor signatures on the + // unbonding slashing tx by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_slashing_sigs = 3; +} + +// BTCDelegatorDelegations is a collection of BTC delegations from the same delegator. +message BTCDelegatorDelegations { + repeated BTCDelegation dels = 1; +} + +// BTCDelegatorDelegationIndex is a list of staking tx hashes of BTC delegations from the same delegator. +message BTCDelegatorDelegationIndex { + repeated bytes staking_tx_hash_list = 1; +} + +// BTCDelegationStatus is the status of a delegation. The state transition path is +// PENDING -> ACTIVE -> UNBONDED with two possibilities: +// 1. the typical path when timelock of staking transaction expires. +// 2. the path when staker requests early undelegation through MsgBTCUndelegate message. +enum BTCDelegationStatus { + // PENDING defines a delegation that is waiting for covenant signatures to become active. + PENDING = 0; + // ACTIVE defines a delegation that has voting power + ACTIVE = 1; + // UNBONDED defines a delegation no longer has voting power: + // - either reaching the end of staking transaction timelock + // - or receiving unbonding tx with signatures from staker and covenant committee + UNBONDED = 2; + // ANY is any of the above status + ANY = 3; +} + +// SignatureInfo is a BIP-340 signature together with its signer's BIP-340 PK +message SignatureInfo { + bytes pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + bytes sig = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} + +// CovenantAdaptorSignatures is a list adaptor signatures signed by the +// covenant with different finality provider's public keys as encryption keys +message CovenantAdaptorSignatures { + // cov_pk is the public key of the covenant emulator, used as the public key of the adaptor signature + bytes cov_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // adaptor_sigs is a list of adaptor signatures, each encrypted by a restaked BTC finality provider's public key + repeated bytes adaptor_sigs = 2; +} + +// SelectiveSlashingEvidence is the evidence that the finality provider +// selectively slashed a BTC delegation +// NOTE: it's possible that a slashed finality provider exploits the +// SelectiveSlashingEvidence endpoint while it is actually slashed due to +// equivocation. But such behaviour does not affect the system's security +// or gives any benefit for the adversary +message SelectiveSlashingEvidence { + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 1; + // fp_btc_pk is the BTC PK of the finality provider who + // launches the selective slashing offence + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // recovered_fp_btc_sk is the finality provider's BTC SK recovered from + // the covenant adaptor/Schnorr signature pair. It is the consequence + // of selective slashing. + bytes recovered_fp_btc_sk = 3; + } diff --git a/proto/babylon/btcstaking/v1/events.proto b/proto/babylon/btcstaking/v1/events.proto new file mode 100644 index 000000000..1d9845b4f --- /dev/null +++ b/proto/babylon/btcstaking/v1/events.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "babylon/btcstaking/v1/btcstaking.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// EventNewFinalityProvider is the event emitted when a finality provider is created +message EventNewFinalityProvider { FinalityProvider fp = 1; } + +// EventNewBTCDelegation is the event emitted when a BTC delegation is created +// NOTE: the BTC delegation is not active thus does not have voting power yet +// only after it receives a covenant signature it becomes activated and has voting power +message EventNewBTCDelegation { BTCDelegation btc_del = 1; } + +// EventActivateBTCDelegation is the event emitted when covenant activates a BTC delegation +// such that the BTC delegation starts to have voting power in its timelock period +message EventActivateBTCDelegation { BTCDelegation btc_del = 1; } + +// EventUnbondingBTCDelegation is the event emitted when an unbonding BTC delegation +// receives all signatures needed for becoming unbonded +message EventUnbondedBTCDelegation { + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + repeated bytes fp_btc_pk_list = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_tx_hash is the hash of the staking tx. + // (fp_pks..., del_pk, staking_tx_hash) uniquely identifies a BTC delegation + string staking_tx_hash = 3; + // unbonding_tx_hash is the hash of the unbonding tx. + string unbonding_tx_hash = 4; + // from_state is the last state the BTC delegation was at + BTCDelegationStatus from_state = 5; +} + +// EventSelectiveSlashing is the event emitted when an adversarial +// finality provider selectively slashes a BTC delegation. This will +// result in slashing of all BTC delegations under this finality provider. +message EventSelectiveSlashing { + // evidence is the evidence of selective slashing + SelectiveSlashingEvidence evidence = 1; +} diff --git a/proto/babylon/btcstaking/v1/genesis.proto b/proto/babylon/btcstaking/v1/genesis.proto new file mode 100644 index 000000000..406cba75a --- /dev/null +++ b/proto/babylon/btcstaking/v1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "babylon/btcstaking/v1/params.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// GenesisState defines the btcstaking module's genesis state. +message GenesisState { + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/babylon/btcstaking/v1/incentive.proto b/proto/babylon/btcstaking/v1/incentive.proto new file mode 100644 index 000000000..659b54d98 --- /dev/null +++ b/proto/babylon/btcstaking/v1/incentive.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/crypto/secp256k1/keys.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// RewardDistCache is the cache for reward distribution of finality providers at a height +message RewardDistCache { + uint64 total_voting_power = 1; + // finality_providers is a list of finality providers' voting power information + repeated FinalityProviderDistInfo finality_providers = 2; +} + +// FinalityProviderDistInfo is the reward distribution of a finality provider and its BTC delegations +message FinalityProviderDistInfo { + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // babylon_pk is the Babylon public key of the finality provider + cosmos.crypto.secp256k1.PubKey babylon_pk = 2; + // commission defines the commission rate of finality provider + string commission = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; + // total_voting_power is the total voting power of the finality provider + uint64 total_voting_power = 4; + // btc_dels is a list of BTC delegations' voting power information under this finality provider + repeated BTCDelDistInfo btc_dels = 5; +} + +// BTCDelDistInfo contains the information related to reward distribution for a BTC delegations +message BTCDelDistInfo { + // babylon_pk is the Babylon public key of the BTC delegations + cosmos.crypto.secp256k1.PubKey babylon_pk = 1; + // voting_power is the voting power of the BTC delegation + uint64 voting_power = 2; +} diff --git a/proto/babylon/btcstaking/v1/params.proto b/proto/babylon/btcstaking/v1/params.proto new file mode 100644 index 000000000..b140adecc --- /dev/null +++ b/proto/babylon/btcstaking/v1/params.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// Params defines the parameters for the module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // covenant_pks is the list of public keys held by the covenant committee + // each PK follows encoding in BIP-340 spec on Bitcoin + repeated bytes covenant_pks = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // covenant_quorum is the minimum number of signatures needed for the covenant + // multisignature + uint32 covenant_quorum = 2; + // slashing address is the address that the slashed BTC goes to + // the address is in string on Bitcoin + string slashing_address = 3; + // min_slashing_tx_fee_sat is the minimum amount of tx fee (quantified + // in Satoshi) needed for the pre-signed slashing tx + // TODO: change to satoshi per byte? + int64 min_slashing_tx_fee_sat = 4; + // min_commission_rate is the chain-wide minimum commission rate that a finality provider can charge their delegators + string min_commission_rate = 5 [ + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // slashing_rate determines the portion of the staked amount to be slashed, + // expressed as a decimal (e.g., 0.5 for 50%). + string slashing_rate = 6 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + uint32 max_active_finality_providers = 7; + // min_unbonding_time is the minimum time for unbonding transaction timelock in BTC blocks + uint32 min_unbonding_time = 8; +} diff --git a/proto/babylon/btcstaking/v1/pop.proto b/proto/babylon/btcstaking/v1/pop.proto new file mode 100644 index 000000000..fa3ae87d8 --- /dev/null +++ b/proto/babylon/btcstaking/v1/pop.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// BTCSigType indicates the type of btc_sig in a pop +enum BTCSigType { + // BIP340 means the btc_sig will follow the BIP-340 encoding + BIP340 = 0; + // BIP322 means the btc_sig will follow the BIP-322 encoding + BIP322 = 1; + // ECDSA means the btc_sig will follow the ECDSA encoding + // ref: https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/src/BtcWallet.ts#L331 + ECDSA = 2; +} + +// ProofOfPossession is the proof of possession that a Babylon secp256k1 +// secret key and a Bitcoin secp256k1 secret key are held by the same +// person +message ProofOfPossession { + // btc_sig_type indicates the type of btc_sig in the pop + BTCSigType btc_sig_type = 1; + // babylon_sig is the signature generated via sign(sk_babylon, pk_btc) + bytes babylon_sig = 2; + // btc_sig is the signature generated via sign(sk_btc, babylon_sig) + // the signature follows encoding in either BIP-340 spec or BIP-322 spec + bytes btc_sig = 3; +} + +// BIP322Sig is a BIP-322 signature together with the address corresponding to +// the signer +message BIP322Sig { + // address is the signer's address + string address = 1; + // sig is the actual signature in BIP-322 format + bytes sig = 2; +} \ No newline at end of file diff --git a/proto/babylon/btcstaking/v1/query.proto b/proto/babylon/btcstaking/v1/query.proto new file mode 100644 index 000000000..640258bea --- /dev/null +++ b/proto/babylon/btcstaking/v1/query.proto @@ -0,0 +1,250 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "babylon/btcstaking/v1/params.proto"; +import "babylon/btcstaking/v1/btcstaking.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// Query defines the gRPC querier service. +service Query { + // Parameters queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/params"; + } + + // FinalityProviders queries all finality providers + rpc FinalityProviders(QueryFinalityProvidersRequest) returns (QueryFinalityProvidersResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers"; + } + + // FinalityProvider info about one finality provider + rpc FinalityProvider(QueryFinalityProviderRequest) returns (QueryFinalityProviderResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers/{fp_btc_pk_hex}/finality_provider"; + } + + // BTCDelegations queries all BTC delegations under a given status + rpc BTCDelegations(QueryBTCDelegationsRequest) returns (QueryBTCDelegationsResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/btc_delegations"; + } + + // ActiveFinalityProvidersAtHeight queries finality providers with non zero voting power at given height. + rpc ActiveFinalityProvidersAtHeight(QueryActiveFinalityProvidersAtHeightRequest) returns (QueryActiveFinalityProvidersAtHeightResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers/{height}"; + } + + // FinalityProviderPowerAtHeight queries the voting power of a finality provider at a given height + rpc FinalityProviderPowerAtHeight(QueryFinalityProviderPowerAtHeightRequest) returns (QueryFinalityProviderPowerAtHeightResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers/{fp_btc_pk_hex}/power/{height}"; + } + + // FinalityProviderCurrentPower queries the voting power of a finality provider at the current height + rpc FinalityProviderCurrentPower(QueryFinalityProviderCurrentPowerRequest) returns (QueryFinalityProviderCurrentPowerResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers/{fp_btc_pk_hex}/power"; + } + + // ActivatedHeight queries the height when BTC staking protocol is activated, i.e., the first height when + // there exists 1 finality provider with voting power + rpc ActivatedHeight(QueryActivatedHeightRequest) returns (QueryActivatedHeightResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/activated_height"; + } + + // FinalityProviderDelegations queries all BTC delegations of the given finality provider + rpc FinalityProviderDelegations(QueryFinalityProviderDelegationsRequest) returns (QueryFinalityProviderDelegationsResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/finality_providers/{fp_btc_pk_hex}/delegations"; + } + + // BTCDelegation retrieves delegation by corresponding staking tx hash + rpc BTCDelegation(QueryBTCDelegationRequest) returns (QueryBTCDelegationResponse) { + option (google.api.http).get = "/babylon/btcstaking/v1/btc_delegations/{staking_tx_hash_hex}"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryFinalityProvidersRequest is the request type for the +// Query/FinalityProviders RPC method. +message QueryFinalityProvidersRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryFinalityProvidersResponse is the response type for the +// Query/FinalityProviders RPC method. +message QueryFinalityProvidersResponse { + // finality_providers contains all the finality providers + repeated FinalityProvider finality_providers = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + + +// QueryFinalityProviderRequest requests information about a finality provider +message QueryFinalityProviderRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider + string fp_btc_pk_hex = 1; +} + +// QueryFinalityProviderResponse contains information about a finality provider +message QueryFinalityProviderResponse { + // finality_provider contains the FinalityProvider + FinalityProvider finality_provider = 1; +} + +// QueryBTCDelegationsRequest is the request type for the +// Query/BTCDelegations RPC method. +message QueryBTCDelegationsRequest { + // status is the queried status for BTC delegations + BTCDelegationStatus status = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryBTCDelegationsResponse is the response type for the +// Query/BTCDelegations RPC method. +message QueryBTCDelegationsResponse { + // btc_delegations contains all the queried BTC delegations under the given status + repeated BTCDelegation btc_delegations = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryFinalityProviderPowerAtHeightRequest is the request type for the +// Query/FinalityProviderPowerAtHeight RPC method. +message QueryFinalityProviderPowerAtHeightRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider that + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + string fp_btc_pk_hex = 1; + + // height is used for querying the given finality provider's voting power at this height + uint64 height = 2; +} + +// QueryFinalityProviderPowerAtHeightResponse is the response type for the +// Query/FinalityProviderPowerAtHeight RPC method. +message QueryFinalityProviderPowerAtHeightResponse { + // voting_power is the voting power of the finality provider + uint64 voting_power = 1; +} + +// QueryFinalityProviderCurrentPowerRequest is the request type for the +// Query/FinalityProviderCurrentPower RPC method. +message QueryFinalityProviderCurrentPowerRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider that + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + string fp_btc_pk_hex = 1; +} + +// QueryFinalityProviderCurrentPowerResponse is the response type for the +// Query/FinalityProviderCurrentPower RPC method. +message QueryFinalityProviderCurrentPowerResponse { + // height is the current height + uint64 height = 1; + // voting_power is the voting power of the finality provider + uint64 voting_power = 2; +} + +// QueryActiveFinalityProvidersAtHeightRequest is the request type for the +// Query/ActiveFinalityProvidersAtHeight RPC method. +message QueryActiveFinalityProvidersAtHeightRequest { + // height defines at which Babylon height to query the finality providers info. + uint64 height = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryActiveFinalityProvidersAtHeightResponse is the response type for the +// Query/ActiveFinalityProvidersAtHeight RPC method. +message QueryActiveFinalityProvidersAtHeightResponse { + // finality_providers contains all the queried finality providersn. + repeated FinalityProviderWithMeta finality_providers = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryActivatedHeightRequest is the request type for the Query/ActivatedHeight RPC method. +message QueryActivatedHeightRequest {} + +// QueryActivatedHeightResponse is the response type for the Query/ActivatedHeight RPC method. +message QueryActivatedHeightResponse { + uint64 height = 1; +} + +// QueryFinalityProviderDelegationsRequest is the request type for the +// Query/FinalityProviderDelegations RPC method. +message QueryFinalityProviderDelegationsRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality providerthat + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + string fp_btc_pk_hex = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryFinalityProviderDelegationsResponse is the response type for the +// Query/FinalityProviderDelegations RPC method. +message QueryFinalityProviderDelegationsResponse { + // btc_delegator_delegations contains all the queried BTC delegations. + repeated BTCDelegatorDelegations btc_delegator_delegations = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryBTCDelegationRequest is the request type to retrieve a BTC delegation by +// staking tx hash +message QueryBTCDelegationRequest { + // Hash of staking transaction in btc format + string staking_tx_hash_hex = 1; +} + +// QueryBTCDelegationResponse is response type matching QueryBTCDelegationRequest +// and containing BTC delegation information +message QueryBTCDelegationResponse { + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + repeated bytes fp_btc_pk_list = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // start_height is the start BTC height of the BTC delegation + // it is the start BTC height of the timelock + uint64 start_height = 3; + // end_height is the end height of the BTC delegation + // it is the end BTC height of the timelock - w + uint64 end_height = 4; + // total_sat is the total amount of BTC stakes in this delegation + // quantified in satoshi + uint64 total_sat = 5; + // staking_tx_hex is the hex string of staking tx + string staking_tx_hex = 6; + // covenant_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_sigs = 7; + // whether this delegation is active + bool active = 8; + // unbonding_time used in unbonding output timelock path and in slashing transactions + // change outputs + uint32 unbonding_time = 9; + // undelegation_info is the undelegation info of this delegation. + BTCUndelegationInfo undelegation_info = 10; +} diff --git a/proto/babylon/btcstaking/v1/tx.proto b/proto/babylon/btcstaking/v1/tx.proto new file mode 100644 index 000000000..05bc22218 --- /dev/null +++ b/proto/babylon/btcstaking/v1/tx.proto @@ -0,0 +1,187 @@ +syntax = "proto3"; +package babylon.btcstaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos/crypto/secp256k1/keys.proto"; +import "babylon/btcstaking/v1/params.proto"; +import "babylon/btccheckpoint/v1/btccheckpoint.proto"; +import "cosmos/staking/v1beta1/staking.proto"; +import "babylon/btcstaking/v1/pop.proto"; + +option go_package = "github.com/babylonchain/babylon/x/btcstaking/types"; + +// Msg defines the Msg service. +// TODO: handle unbonding tx with full witness +service Msg { + option (cosmos.msg.v1.service) = true; + + // CreateFinalityProvider creates a new finality provider + rpc CreateFinalityProvider(MsgCreateFinalityProvider) returns (MsgCreateFinalityProviderResponse); + // CreateBTCDelegation creates a new BTC delegation + rpc CreateBTCDelegation(MsgCreateBTCDelegation) returns (MsgCreateBTCDelegationResponse); + // AddCovenantSigs handles signatures from a covenant member + rpc AddCovenantSigs(MsgAddCovenantSigs) returns (MsgAddCovenantSigsResponse); + // BTCUndelegate handles a signature on unbonding tx from its delegator + rpc BTCUndelegate(MsgBTCUndelegate) returns (MsgBTCUndelegateResponse); + // SelectiveSlashingEvidence handles the evidence of selective slashing launched + // by a finality provider + rpc SelectiveSlashingEvidence(MsgSelectiveSlashingEvidence) returns (MsgSelectiveSlashingEvidenceResponse); + // UpdateParams updates the btcstaking module parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgCreateFinalityProvider is the message for creating a finality provider +message MsgCreateFinalityProvider { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + + // description defines the description terms for the finality provider. + cosmos.staking.v1beta1.Description description = 2; + // commission defines the commission rate of finality provider. + string commission = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; + // babylon_pk is the Babylon secp256k1 PK of this finality provider + cosmos.crypto.secp256k1.PubKey babylon_pk = 4; + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 6; +} +// MsgCreateFinalityProviderResponse is the response for MsgCreateFinalityProvider +message MsgCreateFinalityProviderResponse {} + +// MsgCreateBTCDelegation is the message for creating a BTC delegation +message MsgCreateBTCDelegation { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + cosmos.crypto.secp256k1.PubKey babylon_pk = 2; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 3; + // btc_pk is the Bitcoin secp256k1 PK of the BTC delegator + bytes btc_pk = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // fp_btc_pk_list is the list of Bitcoin secp256k1 PKs of the finality providers, if there is more than one + // finality provider pk it means that delegation is re-staked + repeated bytes fp_btc_pk_list = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_time is the time lock used in staking transaction + uint32 staking_time = 6; + // staking_value is the amount of satoshis locked in staking output + int64 staking_value = 7; + // staking_tx is the staking tx along with the merkle proof of inclusion in btc block + babylon.btccheckpoint.v1.TransactionInfo staking_tx = 8; + // slashing_tx is the slashing tx + // Note that the tx itself does not contain signatures, which are off-chain. + bytes slashing_tx = 9 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + // The staking tx output further needs signatures from covenant and finality provider in + // order to be spendable. + bytes delegator_slashing_sig = 10 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // unbonding_time is the time lock used when funds are being unbonded. It is be used in: + // - unbonding transaction, time lock spending path + // - staking slashing transaction, change output + // - unbonding slashing transaction, change output + // It must be smaller than math.MaxUInt16 and larger that max(MinUnbondingTime, CheckpointFinalizationTimeout) + uint32 unbonding_time = 11; + // fields related to unbonding transaction + // unbonding_tx is a bitcoin unbonding transaction i.e transaction that spends + // staking output and sends it to the unbonding output + bytes unbonding_tx = 12; + // unbonding_value is amount of satoshis locked in unbonding output. + // NOTE: staking_value and unbonding_value could be different because of the difference between the fee for staking tx and that for unbonding + int64 unbonding_value = 13; + // unbonding_slashing_tx is the slashing tx which slash unbonding contract + // Note that the tx itself does not contain signatures, which are off-chain. + bytes unbonding_slashing_tx = 14 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_unbonding_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + bytes delegator_unbonding_slashing_sig = 15 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +// MsgCreateBTCDelegationResponse is the response for MsgCreateBTCDelegation +message MsgCreateBTCDelegationResponse {} + +// MsgAddCovenantSigs is the message for handling signatures from a covenant member +message MsgAddCovenantSigs { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // pk is the BTC public key of the covenant member + bytes pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 3; + // sigs is a list of adaptor signatures of the covenant + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + repeated bytes slashing_tx_sigs = 4; + // unbonding_tx_sig is the signature of the covenant on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + bytes unbonding_tx_sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // slashing_unbonding_tx_sigs is a list of adaptor signatures of the covenant + // on slashing tx corresponding to unbonding tx submitted to babylon + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + repeated bytes slashing_unbonding_tx_sigs = 6; +} +// MsgAddCovenantSigsResponse is the response for MsgAddCovenantSigs +message MsgAddCovenantSigsResponse {} + +// MsgBTCUndelegate is the message for handling signature on unbonding tx +// from its delegator. This signature effectively proves that the delegator +// wants to unbond this BTC delegation +message MsgBTCUndelegate { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 2; + // unbonding_tx_sig is the signature of the staker on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + bytes unbonding_tx_sig = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +// MsgBTCUndelegateResponse is the response for MsgBTCUndelegate +message MsgBTCUndelegateResponse {} + +// MsgSelectiveSlashingEvidence is the message for handling evidence of selective slashing +// launched by a finality provider +message MsgSelectiveSlashingEvidence { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 2; + // recovered_fp_btc_sk is the BTC SK of the finality provider who + // launches the selective slashing offence. The SK is recovered by + // using a covenant adaptor signature and the corresponding Schnorr + // signature + bytes recovered_fp_btc_sk = 3; +} +// MsgSelectiveSlashingEvidenceResponse is the response for MsgSelectiveSlashingEvidence +message MsgSelectiveSlashingEvidenceResponse {} + +// MsgUpdateParams defines a message for updating btcstaking 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 finality 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 {} diff --git a/proto/babylon/checkpointing/v1/bls_key.proto b/proto/babylon/checkpointing/v1/bls_key.proto index 204af94e8..494dbb048 100644 --- a/proto/babylon/checkpointing/v1/bls_key.proto +++ b/proto/babylon/checkpointing/v1/bls_key.proto @@ -8,12 +8,12 @@ option go_package = "github.com/babylonchain/babylon/x/checkpointing/types"; // BlsKey wraps BLS public key with PoP message BlsKey { // pubkey is the BLS public key of a validator - bytes pubkey = 2 + bytes pubkey = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/crypto/bls12381.PublicKey" ]; // pop is the proof-of-possession of the BLS key - ProofOfPossession pop = 3; + ProofOfPossession pop = 2; } // ProofOfPossession defines proof for the ownership of Ed25519 and BLS private @@ -21,10 +21,10 @@ message BlsKey { message ProofOfPossession { // ed25519_sig is used for verification, ed25519_sig = sign(key = Ed25519_sk, // data = BLS_pk) - bytes ed25519_sig = 2; + bytes ed25519_sig = 1; // bls_sig is the result of PoP, bls_sig = sign(key = BLS_sk, data = // ed25519_sig) - bytes bls_sig = 3 + bytes bls_sig = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; } @@ -35,7 +35,28 @@ message ValidatorWithBlsKeySet { repeated ValidatorWithBlsKey val_set = 1; } // ValidatorWithBlsKey couples validator address, voting power, and its bls // public key message ValidatorWithBlsKey { + // validator_address is the address of the validator string validator_address = 1; + // bls_pub_key is the BLS public key of the validator bytes bls_pub_key = 2; + // voting_power is the voting power of the validator at the given epoch uint64 voting_power = 3; } + +// VoteExtension defines the structure used to create a BLS vote extension. +message VoteExtension { + // signer is the address of the vote extension signer + string signer = 1; + // validator_address is the address of the validator + string validator_address = 2; + // block_hash is the hash of the block that the vote extension is signed over + bytes block_hash = 3; + // epoch_num is the epoch number of the vote extension + uint64 epoch_num = 4; + // height is the height of the vote extension + uint64 height =5; + // bls_sig is the BLS signature + bytes bls_sig = 6 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; +} diff --git a/proto/babylon/checkpointing/v1/checkpoint.proto b/proto/babylon/checkpointing/v1/checkpoint.proto index 909450708..208a191d4 100644 --- a/proto/babylon/checkpointing/v1/checkpoint.proto +++ b/proto/babylon/checkpointing/v1/checkpoint.proto @@ -3,18 +3,19 @@ package babylon.checkpointing.v1; import "google/protobuf/timestamp.proto"; import "gogoproto/gogo.proto"; +import "tendermint/abci/types.proto"; option go_package = "github.com/babylonchain/babylon/x/checkpointing/types"; -// RawCheckpoint wraps the BLS multi sig with meta data +// RawCheckpoint wraps the BLS multi sig with metadata message RawCheckpoint { option (gogoproto.equal) = true; // epoch_num defines the epoch number the raw checkpoint is for uint64 epoch_num = 1; - // last_commit_hash defines the 'LastCommitHash' that individual BLS sigs are - // signed on - bytes last_commit_hash = 2 [ (gogoproto.customtype) = "LastCommitHash" ]; + // block_hash defines the 'BlockID.Hash', which is the hash of + // the block that individual BLS sigs are signed on + bytes block_hash = 2 [ (gogoproto.customtype) = "BlockHash" ]; // bitmap defines the bitmap that indicates the signers of the BLS multi sig bytes bitmap = 3; // bls_multi_sig defines the multi sig that is aggregated from individual BLS @@ -24,7 +25,7 @@ message RawCheckpoint { "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; } -// RawCheckpointWithMeta wraps the raw checkpoint with meta data. +// RawCheckpointWithMeta wraps the raw checkpoint with metadata. message RawCheckpointWithMeta { option (gogoproto.equal) = true; @@ -43,6 +44,14 @@ message RawCheckpointWithMeta { repeated CheckpointStateUpdate lifecycle = 5; } +// InjectedCheckpoint wraps the checkpoint and the extended votes +message InjectedCheckpoint { + RawCheckpointWithMeta ckpt = 1; + // extended_commit_info is the commit info including the vote extensions + // from the previous proposal + tendermint.abci.ExtendedCommitInfo extended_commit_info = 2; +} + // CheckpointStatus is the status of a checkpoint. enum CheckpointStatus { option (gogoproto.goproto_enum_prefix) = false; @@ -74,20 +83,24 @@ message CheckpointStateUpdate { google.protobuf.Timestamp block_time = 3 [ (gogoproto.stdtime) = true ]; } -// BlsSig wraps the BLS sig with meta data. +// BlsSig wraps the BLS sig with metadata. message BlsSig { option (gogoproto.equal) = false; // epoch_num defines the epoch number that the BLS sig is signed on uint64 epoch_num = 1; - // last_commit_hash defines the 'LastCommitHash' that the BLS sig is signed on - bytes last_commit_hash = 2 [ (gogoproto.customtype) = "LastCommitHash" ]; + // block_hash defines the 'BlockID.Hash', which is the hash of + // the block that individual BLS sigs are signed on + bytes block_hash = 2 [ (gogoproto.customtype) = "BlockHash" ]; bytes bls_sig = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; // can't find cosmos_proto.scalar when compiling due to cosmos v0.45.4 does // not support scalar string signer_address = 4 [(cosmos_proto.scalar) = - // "cosmos.AddressString"]; the signer_address defines the address of the + // "cosmos.AddressString"] + // the signer_address defines the address of the // signer string signer_address = 4; + // validator_address defines the validator's consensus address + string validator_address = 5; } diff --git a/proto/babylon/checkpointing/v1/tx.proto b/proto/babylon/checkpointing/v1/tx.proto index 78a990491..bfc00666e 100644 --- a/proto/babylon/checkpointing/v1/tx.proto +++ b/proto/babylon/checkpointing/v1/tx.proto @@ -2,44 +2,26 @@ syntax = "proto3"; package babylon.checkpointing.v1; import "gogoproto/gogo.proto"; -import "babylon/checkpointing/v1/checkpoint.proto"; import "babylon/checkpointing/v1/bls_key.proto"; import "cosmos/staking/v1beta1/tx.proto"; -import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; option go_package = "github.com/babylonchain/babylon/x/checkpointing/types"; // Msg defines the checkpointing Msg service. service Msg { - // AddBlsSig defines a method for accumulating BLS signatures - rpc AddBlsSig(MsgAddBlsSig) returns (MsgAddBlsSigResponse); + option (cosmos.msg.v1.service) = true; // WrappedCreateValidator defines a method for registering a new validator rpc WrappedCreateValidator(MsgWrappedCreateValidator) returns (MsgWrappedCreateValidatorResponse); } -// MsgAddBlsSig defines a message to add a bls signature from a -// validator -message MsgAddBlsSig { - option (gogoproto.equal) = false; - option (gogoproto.goproto_getters) = false; - - // signer corresponds to the submitter of the transaction - // This might be a different entity compared to the one that created the BLS signature - // (i.e. the validator owner of the BLS key which is specified by the `bls_sig.signer_address`) - string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - - BlsSig bls_sig = 2; -} - -// MsgAddBlsSigResponse defines the MsgAddBlsSig response type. -message MsgAddBlsSigResponse {} - // MsgWrappedCreateValidator defines a wrapped message to create a validator message MsgWrappedCreateValidator { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg_create_validator"; BlsKey key = 1; cosmos.staking.v1beta1.MsgCreateValidator msg_create_validator = 2; diff --git a/proto/babylon/epoching/v1/epoching.proto b/proto/babylon/epoching/v1/epoching.proto index 9b56c0bca..59382045e 100644 --- a/proto/babylon/epoching/v1/epoching.proto +++ b/proto/babylon/epoching/v1/epoching.proto @@ -3,33 +3,40 @@ package babylon.epoching.v1; import "google/protobuf/timestamp.proto"; import "gogoproto/gogo.proto"; -import "tendermint/types/types.proto"; import "cosmos/staking/v1beta1/tx.proto"; +import "cosmos/base/v1beta1/coin.proto"; option go_package = "github.com/babylonchain/babylon/x/epoching/types"; // Epoch is a structure that contains the metadata of an epoch message Epoch { + // epoch_number is the number of this epoch uint64 epoch_number = 1; + // current_epoch_interval is the epoch interval at the time of this epoch uint64 current_epoch_interval = 2; + // first_block_height is the height of the first block in this epoch uint64 first_block_height = 3; - // last_block_header is the header of the last block in this epoch. - // Babylon needs to remember the last header of each epoch to complete + // last_block_time is the time of the last block in this epoch. + // Babylon needs to remember the last header's time of each epoch to complete // unbonding validators/delegations when a previous epoch's checkpoint is - // finalised. The last_block_header field is nil in the epoch's beginning, and + // finalised. The last_block_time field is nil in the epoch's beginning, and // is set upon the end of this epoch. - tendermint.types.Header last_block_header = 4; + google.protobuf.Timestamp last_block_time = 4 [ (gogoproto.stdtime) = true ]; // app_hash_root is the Merkle root of all AppHashs in this epoch // It will be used for proving a block is in an epoch bytes app_hash_root = 5; - // sealer_header is the 2nd header of the next epoch - // This validator set has generated a BLS multisig on `last_commit_hash` of - // the sealer header - tendermint.types.Header sealer_header = 6; + // sealer is the last block of the sealed epoch + // sealer_app_hash points to the sealer but stored in the 1st header + // of the next epoch + bytes sealer_app_hash = 6; + // sealer_block_hash is the hash of the sealer + // the validator set has generated a BLS multisig on the hash, + // i.e., hash of the last block in the epoch + bytes sealer_block_hash = 7; } // QueuedMessage is a message that can change the validator set and is delayed -// to the epoch boundary +// to the end of an epoch message QueuedMessage { // tx_id is the ID of the tx that contains the message bytes tx_id = 1; @@ -46,7 +53,7 @@ message QueuedMessage { cosmos.staking.v1beta1.MsgDelegate msg_delegate = 6; cosmos.staking.v1beta1.MsgUndelegate msg_undelegate = 7; cosmos.staking.v1beta1.MsgBeginRedelegate msg_begin_redelegate = 8; - // TODO: after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation + cosmos.staking.v1beta1.MsgCancelUnbondingDelegation msg_cancel_unbonding_delegation = 9; } } @@ -90,8 +97,9 @@ message ValidatorLifecycle { message DelegationStateUpdate { BondState state = 1; string val_addr = 2; - uint64 block_height = 3; - google.protobuf.Timestamp block_time = 4 [ (gogoproto.stdtime) = true ]; + cosmos.base.v1beta1.Coin amount = 3; + uint64 block_height = 4; + google.protobuf.Timestamp block_time = 5 [ (gogoproto.stdtime) = true ]; } // ValidatorLifecycle is a message that records records the lifecycle of diff --git a/proto/babylon/epoching/v1/events.proto b/proto/babylon/epoching/v1/events.proto index ec210e2f8..f41c527e3 100644 --- a/proto/babylon/epoching/v1/events.proto +++ b/proto/babylon/epoching/v1/events.proto @@ -63,3 +63,13 @@ message EventWrappedBeginRedelegate { string denom = 5; uint64 epoch_boundary = 6; } + +// EventWrappedCancelUnbondingDelegation is the event emitted when a +// MsgWrappedCancelUnbondingDelegation has been queued +message EventWrappedCancelUnbondingDelegation { + string delegator_address = 1; + string validator_address = 2; + uint64 amount = 3; + int64 creation_height = 4; + uint64 epoch_boundary = 5; +} diff --git a/proto/babylon/epoching/v1/tx.proto b/proto/babylon/epoching/v1/tx.proto index a9bc48604..47f24da33 100644 --- a/proto/babylon/epoching/v1/tx.proto +++ b/proto/babylon/epoching/v1/tx.proto @@ -12,6 +12,8 @@ option go_package = "github.com/babylonchain/babylon/x/epoching/types"; // Msg defines the Msg service. service Msg { + option (cosmos.msg.v1.service) = true; + // WrappedDelegate defines a method for performing a delegation of coins from // a delegator to a validator. rpc WrappedDelegate(MsgWrappedDelegate) returns (MsgWrappedDelegateResponse); @@ -26,6 +28,11 @@ service Msg { rpc WrappedBeginRedelegate(MsgWrappedBeginRedelegate) returns (MsgWrappedBeginRedelegateResponse); + // WrappedCancelUnbondingDelegation defines a method for cancelling unbonding of + // coins from a delegator and source validator to a destination validator. + rpc WrappedCancelUnbondingDelegation(MsgWrappedCancelUnbondingDelegation) + returns (MsgWrappedCancelUnbondingDelegationResponse); + // UpdateParams defines a method for updating epoching module parameters. rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); } @@ -34,6 +41,7 @@ service Msg { message MsgWrappedDelegate { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; cosmos.staking.v1beta1.MsgDelegate msg = 1; } @@ -45,6 +53,7 @@ message MsgWrappedDelegateResponse {} message MsgWrappedUndelegate { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; cosmos.staking.v1beta1.MsgUndelegate msg = 1; } @@ -58,6 +67,7 @@ message MsgWrappedUndelegateResponse {} message MsgWrappedBeginRedelegate { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; cosmos.staking.v1beta1.MsgBeginRedelegate msg = 1; } @@ -66,6 +76,20 @@ message MsgWrappedBeginRedelegate { // MsgWrappedBeginRedelegate message message MsgWrappedBeginRedelegateResponse {} +// MsgWrappedCancelUnbondingDelegation is the message for cancelling +// an unbonding delegation +message MsgWrappedCancelUnbondingDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; + + cosmos.staking.v1beta1.MsgCancelUnbondingDelegation msg = 1; +} + +// MsgWrappedCancelUnbondingDelegationResponse is the response to the +// MsgWrappedCancelUnbondingDelegation message +message MsgWrappedCancelUnbondingDelegationResponse {} + // MsgUpdateParams defines a message for updating epoching module parameters. message MsgUpdateParams { option (cosmos.msg.v1.signer) = "authority"; @@ -76,7 +100,7 @@ message MsgUpdateParams { // in cosmos-proto string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // params defines the epoching paramaeters parameters to update. + // params defines the epoching parameters to update. // // NOTE: All parameters must be supplied. Params params = 2 [(gogoproto.nullable) = false]; diff --git a/proto/babylon/finality/v1/events.proto b/proto/babylon/finality/v1/events.proto new file mode 100644 index 000000000..61d7f6d5e --- /dev/null +++ b/proto/babylon/finality/v1/events.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package babylon.finality.v1; + +import "babylon/finality/v1/finality.proto"; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +// EventSlashedFinalityProvider is the event emitted when a finality provider is slashed +// due to signing two conflicting blocks +message EventSlashedFinalityProvider { + // evidence is the evidence that the finality provider double signs + Evidence evidence = 1; +} diff --git a/proto/babylon/finality/v1/finality.proto b/proto/babylon/finality/v1/finality.proto new file mode 100644 index 000000000..5f2a3d159 --- /dev/null +++ b/proto/babylon/finality/v1/finality.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package babylon.finality.v1; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +import "gogoproto/gogo.proto"; + +// IndexedBlock is the necessary metadata and finalization status of a block +message IndexedBlock { + // height is the height of the block + uint64 height = 1; + // app_hash is the AppHash of the block + bytes app_hash = 2; + // finalized indicates whether the IndexedBlock is finalised by 2/3 + // finality providers or not + bool finalized = 3; +} + +// Evidence is the evidence that a finality provider has signed finality +// signatures with correct public randomness on two conflicting Babylon headers +message Evidence { + // fp_btc_pk is the BTC PK of the finality provider that casts this vote + bytes fp_btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // block_height is the height of the conflicting blocks + uint64 block_height = 2; + // pub_rand is the public randomness the finality provider has committed to + bytes pub_rand = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrPubRand" ]; + // canonical_app_hash is the AppHash of the canonical block + bytes canonical_app_hash = 4; + // fork_app_hash is the AppHash of the fork block + bytes fork_app_hash = 5; + // canonical_finality_sig is the finality signature to the canonical block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + bytes canonical_finality_sig = 6 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; + // fork_finality_sig is the finality signature to the fork block + // where finality signature is an EOTS signature + bytes fork_finality_sig = 7 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; +} diff --git a/proto/babylon/finality/v1/genesis.proto b/proto/babylon/finality/v1/genesis.proto new file mode 100644 index 000000000..f33e6ed15 --- /dev/null +++ b/proto/babylon/finality/v1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package babylon.finality.v1; + +import "gogoproto/gogo.proto"; +import "babylon/finality/v1/params.proto"; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +// GenesisState defines the finality module's genesis state. +message GenesisState { + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/babylon/finality/v1/params.proto b/proto/babylon/finality/v1/params.proto new file mode 100644 index 000000000..c5dce2cc9 --- /dev/null +++ b/proto/babylon/finality/v1/params.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package babylon.finality.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +// Params defines the parameters for the module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // min_pub_rand is the minimum number of public randomness each + // message should commit + uint64 min_pub_rand = 1; +} diff --git a/proto/babylon/finality/v1/query.proto b/proto/babylon/finality/v1/query.proto new file mode 100644 index 000000000..7c618d127 --- /dev/null +++ b/proto/babylon/finality/v1/query.proto @@ -0,0 +1,173 @@ +syntax = "proto3"; +package babylon.finality.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "babylon/finality/v1/params.proto"; +import "babylon/finality/v1/finality.proto"; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +// Query defines the gRPC querier service. +service Query { + // Parameters queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/babylon/finality/v1/params"; + } + + // ListPublicRandomness is a range query for public randomness of a given finality provider + rpc ListPublicRandomness(QueryListPublicRandomnessRequest) returns (QueryListPublicRandomnessResponse) { + option (google.api.http).get = "/babylon/finality/v1/finality_providers/{fp_btc_pk_hex}/public_randomness_list"; + } + + // Block queries a block at a given height + rpc Block(QueryBlockRequest) returns (QueryBlockResponse) { + option (google.api.http).get = "/babylon/finality/v1/blocks/{height}"; + } + + // ListBlocks is a range query for blocks at a given status + rpc ListBlocks(QueryListBlocksRequest) returns (QueryListBlocksResponse) { + option (google.api.http).get = "/babylon/finality/v1/blocks"; + } + + // VotesAtHeight queries finality providers who have signed the block at given height. + rpc VotesAtHeight(QueryVotesAtHeightRequest) returns (QueryVotesAtHeightResponse) { + option (google.api.http).get = "/babylon/finality/v1/votes/{height}"; + } + + // Evidence queries the first evidence which can be used for extracting the BTC SK + rpc Evidence(QueryEvidenceRequest) returns (QueryEvidenceResponse) { + option (google.api.http).get = "/babylon/finality/v1/finality_providers/{fp_btc_pk_hex}/evidence"; + } + + // ListEvidences queries is a range query for evidences + rpc ListEvidences(QueryListEvidencesRequest) returns (QueryListEvidencesResponse) { + option (google.api.http).get = "/babylon/finality/v1/evidences"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryListPublicRandomnessRequest is the request type for the +// Query/ListPublicRandomness RPC method. +message QueryListPublicRandomnessRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider + string fp_btc_pk_hex = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryListPublicRandomnessResponse is the response type for the +// Query/ListPublicRandomness RPC method. +message QueryListPublicRandomnessResponse { + // pub_rand_map is the map where the key is the height and the value + // is the public randomness at this height for the given finality provider + map pub_rand_map = 1 [(gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrPubRand" ]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + + +// QueriedBlockStatus is the status of blocks that the querier wants to query. +enum QueriedBlockStatus { + // NON_FINALIZED means the block is not finalised + NON_FINALIZED = 0; + // FINALIZED means the block is finalized + FINALIZED = 1; + // ANY means the block can be in any status + ANY = 2; +} + +// QueryBlockRequest is the request type for the +// Query/Block RPC method. +message QueryBlockRequest { + // height is the height of the Babylon block + uint64 height = 1; +} + +// QueryBlockResponse is the response type for the +// Query/Block RPC method. +message QueryBlockResponse { + // block is the Babylon at the given height + IndexedBlock block = 1; +} + +// QueryListBlocksRequest is the request type for the +// Query/ListBlocks RPC method. +message QueryListBlocksRequest { + // status indicates the status of blocks that the querier wants to query + QueriedBlockStatus status = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryListBlocksResponse is the response type for the +// Query/ListBlocks RPC method. +message QueryListBlocksResponse { + // blocks is the list of blocks at the given status + repeated IndexedBlock blocks = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryVotesAtHeightRequest is the request type for the +// Query/VotesAtHeight RPC method. +message QueryVotesAtHeightRequest { + // height defines at which height to query the finality providers. + uint64 height = 1; +} + +// QueryVotesAtHeightResponse is the response type for the +// Query/VotesAtHeight RPC method. +message QueryVotesAtHeightResponse { + // btc_pk is the Bitcoin secp256k1 PK of finality providers who have signed the block at given height. + // the PK follows encoding in BIP-340 spec + repeated bytes btc_pks = 1 [(gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey"]; +} + +// QueryEvidenceRequest is the request type for the +// Query/Evidence RPC method. +message QueryEvidenceRequest { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK + // (in BIP340 format) of the finality provider + string fp_btc_pk_hex = 1; +} + +// QueryEvidenceResponse is the response type for the +// Query/Evidence RPC method. +message QueryEvidenceResponse { + Evidence evidence = 1; +} + +// QueryListEvidencesRequest is the request type for the +// Query/ListEvidences RPC method. +message QueryListEvidencesRequest { + // start_height is the starting height that the querier specifies + // such that the RPC will only return evidences since this height + uint64 start_height = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryListEvidencesResponse is the response type for the +// Query/ListEvidences RPC method. +message QueryListEvidencesResponse { + // blocks is the list of evidences + repeated Evidence evidences = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/babylon/finality/v1/tx.proto b/proto/babylon/finality/v1/tx.proto new file mode 100644 index 000000000..11a55fe31 --- /dev/null +++ b/proto/babylon/finality/v1/tx.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; +package babylon.finality.v1; + +option go_package = "github.com/babylonchain/babylon/x/finality/types"; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "babylon/finality/v1/params.proto"; + +// Msg defines the Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // AddFinalitySig adds a finality signature to a given block + rpc AddFinalitySig(MsgAddFinalitySig) returns (MsgAddFinalitySigResponse); + // CommitPubRandList commits a list of public randomness for EOTS + rpc CommitPubRandList(MsgCommitPubRandList) returns (MsgCommitPubRandListResponse); + // TODO: msg for evidence of equivocation. this is not specified yet + // UpdateParams updates the finality module parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgAddFinalitySig defines a message for adding a finality vote +message MsgAddFinalitySig { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // fp_btc_pk is the BTC PK of the finality provider that casts this vote + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // block_height is the height of the voted block + uint64 block_height = 3; + // block_app_hash is the AppHash of the voted block + bytes block_app_hash = 4; + // finality_sig is the finality signature to this block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + bytes finality_sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; +} +// MsgAddFinalitySigResponse is the response to the MsgAddFinalitySig message +message MsgAddFinalitySigResponse{} + +// MsgCommitPubRandList defines a message for committing a list of public randomness for EOTS +message MsgCommitPubRandList { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // fp_btc_pk is the BTC PK of the finality provider that commits the public randomness + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // start_height is the start block height of the list of public randomness + uint64 start_height = 3; + // pub_rand_list is the list of public randomness + repeated bytes pub_rand_list = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrPubRand" ]; + // sig is the signature on (start_height || pub_rand_list) signed by + // SK corresponding to fp_btc_pk. This prevents others to commit public + // randomness on behalf of fp_btc_pk + // TODO: another option is to restrict signer to correspond to fp_btc_pk. This restricts + // the tx submitter to be the holder of fp_btc_pk. Decide this later + bytes sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +// MsgCommitPubRandListResponse is the response to the MsgCommitPubRandList message +message MsgCommitPubRandListResponse{} + +// MsgUpdateParams defines a message for updating finality 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 finality 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 {} diff --git a/proto/babylon/incentive/genesis.proto b/proto/babylon/incentive/genesis.proto new file mode 100644 index 000000000..04f82d21a --- /dev/null +++ b/proto/babylon/incentive/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package babylon.incentive; + +import "gogoproto/gogo.proto"; +import "babylon/incentive/params.proto"; + +option go_package = "github.com/babylonchain/babylon/x/incentive/types"; + +// GenesisState defines the incentive module's genesis state. +message GenesisState { + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/babylon/incentive/incentive.proto b/proto/babylon/incentive/incentive.proto new file mode 100644 index 000000000..ccc4c3a6c --- /dev/null +++ b/proto/babylon/incentive/incentive.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package babylon.incentive; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/babylonchain/babylon/x/incentive/types"; + +// Gauge is an object that stores rewards to be distributed +// code adapted from https://github.com/osmosis-labs/osmosis/blob/v18.0.0/proto/osmosis/incentives/gauge.proto +message Gauge { + // coins are coins that have been in the gauge + // Can have multiple coin denoms + repeated cosmos.base.v1beta1.Coin coins = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// RewardGauge is an object that stores rewards distributed to a BTC staking/timestamping stakeholder +// code adapted from https://github.com/osmosis-labs/osmosis/blob/v18.0.0/proto/osmosis/incentives/gauge.proto +message RewardGauge { + // coins are coins that have been in the gauge + // Can have multiple coin denoms + repeated cosmos.base.v1beta1.Coin coins = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // withdrawn_coins are coins that have been withdrawn by the stakeholder already + repeated cosmos.base.v1beta1.Coin withdrawn_coins = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} \ No newline at end of file diff --git a/proto/babylon/incentive/params.proto b/proto/babylon/incentive/params.proto new file mode 100644 index 000000000..c576cb9a4 --- /dev/null +++ b/proto/babylon/incentive/params.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; +package babylon.incentive; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/babylonchain/babylon/x/incentive/types"; + +// Params defines the parameters for the module, including portions of rewards +// distributed to each type of stakeholder. Note that sum of the portions should +// be strictly less than 1 so that the rest will go to Comet validators/delegations +// adapted from https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/proto/cosmos/distribution/v1beta1/distribution.proto +message Params { + option (gogoproto.goproto_stringer) = false; + + // submitter_portion is the portion of rewards that goes to submitter + string submitter_portion = 1 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // reporter_portion is the portion of rewards that goes to reporter + string reporter_portion = 2 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // btc_staking_portion is the portion of rewards that goes to Finality Providers/delegations + // NOTE: the portion of each Finality Provider/delegation is calculated by using its voting + // power and finality provider's commission + string btc_staking_portion = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; +} diff --git a/proto/babylon/incentive/query.proto b/proto/babylon/incentive/query.proto new file mode 100644 index 000000000..22985e390 --- /dev/null +++ b/proto/babylon/incentive/query.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; +package babylon.incentive; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "babylon/incentive/params.proto"; +import "babylon/incentive/incentive.proto"; + +option go_package = "github.com/babylonchain/babylon/x/incentive/types"; + +// Query defines the gRPC querier service. +service Query { + // Parameters queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/babylonchain/babylon/incentive/params"; + } + // RewardGauge queries the reward gauge of a given stakeholder address + rpc RewardGauges(QueryRewardGaugesRequest) returns (QueryRewardGaugesResponse) { + option (google.api.http).get = "/babylonchain/babylon/incentive/address/{address}/reward_gauge"; + } + // BTCStakingGauge queries the BTC staking gauge of a given height + rpc BTCStakingGauge(QueryBTCStakingGaugeRequest) returns (QueryBTCStakingGaugeResponse) { + option (google.api.http).get = "/babylonchain/babylon/incentive/btc_staking_gauge/{height}"; + } + // BTCTimestampingGauge queries the BTC timestamping gauge of a given epoch + rpc BTCTimestampingGauge(QueryBTCTimestampingGaugeRequest) returns (QueryBTCTimestampingGaugeResponse) { + option (google.api.http).get = "/babylonchain/babylon/incentive/btc_timestamping_gauge/{epoch_num}"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryRewardGaugesRequest is request type for the Query/RewardGauges RPC method. +message QueryRewardGaugesRequest { + // address is the address of the stakeholder in bech32 string + string address = 1; +} + +// QueryRewardGaugesResponse is response type for the Query/RewardGauges RPC method. +message QueryRewardGaugesResponse { + // reward_gauges is the map of reward gauges, where key is the stakeholder type + // and value is the reward gauge holding all rewards for the stakeholder in that type + map reward_gauges = 1; +} + +// QueryBTCStakingGaugeRequest is request type for the Query/BTCStakingGauge RPC method. +message QueryBTCStakingGaugeRequest { + // height is the queried Babylon height + uint64 height = 1; +} + +// QueryBTCStakingGaugeResponse is response type for the Query/BTCStakingGauge RPC method. +message QueryBTCStakingGaugeResponse { + // gauge is the BTC staking gauge at the queried height + Gauge gauge = 1; +} + +// QueryBTCTimestampingGaugeRequest is request type for the Query/BTCTimestampingGauge RPC method. +message QueryBTCTimestampingGaugeRequest { + // epoch_num is the queried epoch number + uint64 epoch_num = 1; +} + +// QueryBTCTimestampingGaugeResponse is response type for the Query/BTCTimestampingGauge RPC method. +message QueryBTCTimestampingGaugeResponse { + // gauge is the BTC timestamping gauge at the queried epoch + Gauge gauge = 1; +} \ No newline at end of file diff --git a/proto/babylon/incentive/tx.proto b/proto/babylon/incentive/tx.proto new file mode 100644 index 000000000..965147bd5 --- /dev/null +++ b/proto/babylon/incentive/tx.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; +package babylon.incentive; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "babylon/incentive/params.proto"; + +option go_package = "github.com/babylonchain/babylon/x/incentive/types"; + +// Msg defines the Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // WithdrawReward defines a method to withdraw rewards of a stakeholder + rpc WithdrawReward(MsgWithdrawReward) returns (MsgWithdrawRewardResponse); + // UpdateParams updates the incentive module parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + + +// MsgWithdrawReward defines a message for withdrawing reward of a stakeholder. +message MsgWithdrawReward { + option (cosmos.msg.v1.signer) = "address"; + // {submitter, reporter, finality_provider, btc_delegation} + string type = 1; + // address is the address of the stakeholder in bech32 string + // signer of this msg has to be this address + string address = 2; +} + +// MsgWithdrawRewardResponse is the response to the MsgWithdrawReward message +message MsgWithdrawRewardResponse { + // coins is the withdrawed coins + repeated cosmos.base.v1beta1.Coin coins = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// MsgUpdateParams defines a message for updating incentive 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 incentive 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 {} diff --git a/proto/babylon/zoneconcierge/v1/tx.proto b/proto/babylon/zoneconcierge/v1/tx.proto index 4fc9a78b9..105a29107 100644 --- a/proto/babylon/zoneconcierge/v1/tx.proto +++ b/proto/babylon/zoneconcierge/v1/tx.proto @@ -11,7 +11,9 @@ option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; // Msg defines the Msg service. service Msg { - // UpdateParams updates the btccheckpoint module parameters. + option (cosmos.msg.v1.service) = true; + + // UpdateParams updates the zoneconcierge module parameters. rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); } @@ -25,7 +27,7 @@ message MsgUpdateParams { // in cosmos-proto string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - // params defines the epoching paramaeters parameters to update. + // params defines the zoneconcierge parameters to update. // // NOTE: All parameters must be supplied. Params params = 2 [(gogoproto.nullable) = false]; @@ -33,4 +35,3 @@ message MsgUpdateParams { // MsgUpdateParamsResponse is the response to the MsgUpdateParams message. message MsgUpdateParamsResponse {} - \ No newline at end of file diff --git a/proto/babylon/zoneconcierge/v1/zoneconcierge.proto b/proto/babylon/zoneconcierge/v1/zoneconcierge.proto index 53fa995a8..7d37d7b30 100644 --- a/proto/babylon/zoneconcierge/v1/zoneconcierge.proto +++ b/proto/babylon/zoneconcierge/v1/zoneconcierge.proto @@ -3,12 +3,12 @@ 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"; import "babylon/checkpointing/v1/bls_key.proto"; import "babylon/checkpointing/v1/checkpoint.proto"; import "babylon/epoching/v1/epoching.proto"; +import "babylon/btclightclient/v1/btclightclient.proto"; option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; @@ -25,15 +25,18 @@ message IndexedHeader { // 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 + // babylon_header_hash is the hash of the babylon block that includes this CZ // header - tendermint.types.Header babylon_header = 5; + bytes babylon_header_hash = 5; + // babylon_header_height is the height of the babylon block that includes this CZ + // header + uint64 babylon_header_height = 6; // epoch is the epoch number of this header on Babylon ledger - uint64 babylon_epoch = 6; + uint64 babylon_epoch = 7; // 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 = 7; + bytes babylon_tx_hash = 8; } // Forks is a list of non-canonical `IndexedHeader`s at the same height. @@ -93,14 +96,14 @@ message FinalizedChainInfo { // - Metadata of this epoch, which includes the sealer header // - Raw checkpoint of this epoch // The verifier can perform the following verification rules: -// - The raw checkpoint's `last_commit_hash` is same as in the sealer header -// - More than 1/3 (in voting power) validators in the validator set of this -// epoch have signed `last_commit_hash` of the sealer header +// - The raw checkpoint's `app_hash` is same as in the sealer header +// - More than 2/3 (in voting power) validators in the validator set of this +// epoch have signed `app_hash` of the sealer header // - The epoch medatata is committed to the `app_hash` of the sealer header // - The validator set is committed to the `app_hash` of the sealer header message ProofEpochSealed { // validator_set is the validator set of the sealed epoch - // This validator set has generated a BLS multisig on `last_commit_hash` of + // This validator set has generated a BLS multisig on `app_hash` of // the sealer header repeated babylon.checkpointing.v1.ValidatorWithBlsKey validator_set = 1; // proof_epoch_info is the Merkle proof that the epoch's metadata is committed @@ -118,16 +121,18 @@ message ProofFinalizedChainInfo { The following fields include proofs that attest the chain info is BTC-finalised */ - // proof_tx_in_block is the proof that tx that carries the header is included - // in a certain Babylon block - tendermint.types.TxProof proof_tx_in_block = 4; - // proof_header_in_epoch is the proof that the Babylon header is in a certain - // epoch - tendermint.crypto.Proof proof_header_in_epoch = 5; + // proof_cz_header_in_epoch is the proof that the CZ header is timestamped + // within a certain epoch + tendermint.crypto.ProofOps proof_cz_header_in_epoch = 1; // proof_epoch_sealed is the proof that the epoch is sealed - babylon.zoneconcierge.v1.ProofEpochSealed proof_epoch_sealed = 6; + babylon.zoneconcierge.v1.ProofEpochSealed proof_epoch_sealed = 2; // proof_epoch_submitted is the proof that the epoch's checkpoint is included // in BTC ledger It is the two TransactionInfo in the best (i.e., earliest) // checkpoint submission - repeated babylon.btccheckpoint.v1.TransactionInfo proof_epoch_submitted = 7; + repeated babylon.btccheckpoint.v1.TransactionInfo proof_epoch_submitted = 3; +} + +// Btc light client chain segment grown during last finalized epoch +message BTCChainSegment { + repeated babylon.btclightclient.v1.BTCHeaderInfo btc_headers = 1; } diff --git a/proto/buf.lock b/proto/buf.lock index 7a2810e68..bdb2a63ae 100644 --- a/proto/buf.lock +++ b/proto/buf.lock @@ -9,8 +9,8 @@ deps: - remote: buf.build owner: cosmos repository: cosmos-sdk - commit: 954f7b05f38440fc8250134b15adec47 - digest: shake256:2ab4404fd04a7d1d52df0e2d0f2d477a3d83ffd88d876957bf3fedfd702c8e52833d65b3ce1d89a3c5adf2aab512616b0e4f51d8463f07eda9a8a3317ee3ac54 + commit: 5a6ab7bc14314acaa912d5e53aef1c2f + digest: shake256:02c00c73493720055f9b57553a35b5550023a3c1914123b247956288a78fb913aff70e66552777ae14d759467e119079d484af081264a5dd607a94d9fbc8116b - remote: buf.build owner: cosmos repository: gogo-proto diff --git a/proto/buf.yaml b/proto/buf.yaml index dc5409427..2db8cfc68 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -1,7 +1,7 @@ version: v1 name: buf.build/babylon/babylond deps: - - buf.build/cosmos/cosmos-sdk:v0.47.0 + - buf.build/cosmos/cosmos-sdk:v0.50.0 - buf.build/cosmos/cosmos-proto:1935555c206d4afb9e94615dfd0fad31 - buf.build/cosmos/gogo-proto:a14993478f40695898ed8a86931094b6656e8a5d - buf.build/googleapis/googleapis:8d7204855ec14631a499bd7393ce1970 diff --git a/proto/scripts/protocgen.sh b/proto/scripts/protocgen.sh index 70312bb3d..9cbce2e51 100755 --- a/proto/scripts/protocgen.sh +++ b/proto/scripts/protocgen.sh @@ -19,4 +19,4 @@ cd .. cp -r github.com/babylonchain/babylon/* ./ rm -rf github.com -go mod tidy -compat=1.20 +go mod tidy -compat=1.21 diff --git a/simapp/config.go b/simapp/config.go deleted file mode 100644 index 98df982bd..000000000 --- a/simapp/config.go +++ /dev/null @@ -1,75 +0,0 @@ -package simapp - -import ( - "flag" - - "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// List of available flags for the simulator -var ( - FlagGenesisFileValue string - FlagParamsFileValue string - FlagExportParamsPathValue string - FlagExportParamsHeightValue int - FlagExportStatePathValue string - FlagExportStatsPathValue string - FlagSeedValue int64 - FlagInitialBlockHeightValue int - FlagNumBlocksValue int - FlagBlockSizeValue int - FlagLeanValue bool - FlagCommitValue bool - FlagOnOperationValue bool // TODO: Remove in favor of binary search for invariant violation - FlagAllInvariantsValue bool - - FlagEnabledValue bool - FlagVerboseValue bool - FlagPeriodValue uint - FlagGenesisTimeValue int64 -) - -// GetSimulatorFlags gets the values of all the available simulation flags -func GetSimulatorFlags() { - // config fields - flag.StringVar(&FlagGenesisFileValue, "Genesis", "", "custom simulation genesis file; cannot be used with params file") - flag.StringVar(&FlagParamsFileValue, "Params", "", "custom simulation params file which overrides any random params; cannot be used with genesis") - flag.StringVar(&FlagExportParamsPathValue, "ExportParamsPath", "", "custom file path to save the exported params JSON") - flag.IntVar(&FlagExportParamsHeightValue, "ExportParamsHeight", 0, "height to which export the randomly generated params") - flag.StringVar(&FlagExportStatePathValue, "ExportStatePath", "", "custom file path to save the exported app state JSON") - flag.StringVar(&FlagExportStatsPathValue, "ExportStatsPath", "", "custom file path to save the exported simulation statistics JSON") - flag.Int64Var(&FlagSeedValue, "Seed", 42, "simulation random seed") - flag.IntVar(&FlagInitialBlockHeightValue, "InitialBlockHeight", 1, "initial block to start the simulation") - flag.IntVar(&FlagNumBlocksValue, "NumBlocks", 500, "number of new blocks to simulate from the initial block height") - flag.IntVar(&FlagBlockSizeValue, "BlockSize", 200, "operations per block") - flag.BoolVar(&FlagLeanValue, "Lean", false, "lean simulation log output") - flag.BoolVar(&FlagCommitValue, "Commit", false, "have the simulation commit") - flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "run slow invariants every operation") - flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "print all invariants if a broken invariant is found") - - // simulation flags - flag.BoolVar(&FlagEnabledValue, "Enabled", false, "enable the simulation") - flag.BoolVar(&FlagVerboseValue, "Verbose", false, "verbose log output") - flag.UintVar(&FlagPeriodValue, "Period", 0, "run slow invariants only once every period assertions") - flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time") -} - -// NewConfigFromFlags creates a simulation from the retrieved values of the flags. -func NewConfigFromFlags() simulation.Config { - return simulation.Config{ - GenesisFile: FlagGenesisFileValue, - ParamsFile: FlagParamsFileValue, - ExportParamsPath: FlagExportParamsPathValue, - ExportParamsHeight: FlagExportParamsHeightValue, - ExportStatePath: FlagExportStatePathValue, - ExportStatsPath: FlagExportStatsPathValue, - Seed: FlagSeedValue, - InitialBlockHeight: FlagInitialBlockHeightValue, - NumBlocks: FlagNumBlocksValue, - BlockSize: FlagBlockSizeValue, - Lean: FlagLeanValue, - Commit: FlagCommitValue, - OnOperation: FlagOnOperationValue, - AllInvariants: FlagAllInvariantsValue, - } -} diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go deleted file mode 100644 index 4efb8f4c7..000000000 --- a/simapp/sim_bench_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package simapp - -import ( - "fmt" - "os" - "testing" - - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/testutil/sims" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" - - "github.com/babylonchain/babylon/app" -) - -// Profile with: -// /usr/local/go/bin/go test -benchmem -run=^$ github.com/babylonchain/babylon/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out -func BenchmarkFullAppSimulation(b *testing.B) { - b.ReportAllocs() - config, db, dir, logger, skip, err := SetupSimulation("goleveldb-app-sim", "Simulation") - if err != nil { - b.Fatalf("simulation setup failed: %s", err.Error()) - } - - if skip { - b.Skip("skipping benchmark application simulation") - } - - defer func() { - db.Close() - err = os.RemoveAll(dir) - if err != nil { - b.Fatal(err) - } - }() - - privSigner, err := app.SetupPrivSigner() - if err != nil { - b.Fatal(err) - } - babylon := app.NewBabylonApp( - logger, - db, - nil, - true, - map[int64]bool{}, - app.DefaultNodeHome, - FlagPeriodValue, - app.GetEncodingConfig(), - privSigner, - sims.EmptyAppOptions{}, - app.GetWasmEnabledProposals(), - app.EmptyWasmOpts, - interBlockCacheOpt()) - - // run randomized simulation - _, simParams, simErr := simulation.SimulateFromSeed( - b, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - - // export state and simParams before the simulation error is checked - if err = sims.CheckExportSimulation(babylon, config, simParams); err != nil { - b.Fatal(err) - } - - if simErr != nil { - b.Fatal(simErr) - } - - if config.Commit { - sims.PrintStats(db) - } -} - -func BenchmarkInvariants(b *testing.B) { - b.ReportAllocs() - config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-invariant-bench", "Simulation") - if err != nil { - b.Fatalf("simulation setup failed: %s", err.Error()) - } - - if skip { - b.Skip("skipping benchmark application simulation") - } - - config.AllInvariants = false - - defer func() { - db.Close() - err = os.RemoveAll(dir) - if err != nil { - b.Fatal(err) - } - }() - - privSigner, err := app.SetupPrivSigner() - if err != nil { - b.Fatal(err) - } - babylon := app.NewBabylonApp( - logger, - db, - nil, - true, - map[int64]bool{}, - app.DefaultNodeHome, - FlagPeriodValue, - app.GetEncodingConfig(), - privSigner, - sims.EmptyAppOptions{}, - app.GetWasmEnabledProposals(), - app.EmptyWasmOpts, - interBlockCacheOpt()) - - // run randomized simulation - _, simParams, simErr := simulation.SimulateFromSeed( - b, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - - // export state and simParams before the simulation error is checked - if err = sims.CheckExportSimulation(babylon, config, simParams); err != nil { - b.Fatal(err) - } - - if simErr != nil { - b.Fatal(simErr) - } - - if config.Commit { - sims.PrintStats(db) - } - - ctx := babylon.NewContext(true, tmproto.Header{Height: babylon.LastBlockHeight() + 1}) - - // 3. Benchmark each invariant separately - // - // NOTE: We use the crisis keeper as it has all the invariants registered with - // their respective metadata which makes it useful for testing/benchmarking. - for _, cr := range babylon.CrisisKeeper.Routes() { - cr := cr - b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) { - if res, stop := cr.Invar(ctx); stop { - b.Fatalf( - "broken invariant at block %d of %d\n%s", - ctx.BlockHeight()-1, config.NumBlocks, res, - ) - } - }) - } -} diff --git a/simapp/sim_test.go b/simapp/sim_test.go deleted file mode 100644 index e95e0ac66..000000000 --- a/simapp/sim_test.go +++ /dev/null @@ -1,359 +0,0 @@ -package simapp - -import ( - "encoding/json" - "fmt" - "math/rand" - "os" - "testing" - - simappparams "github.com/babylonchain/babylon/app/params" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/stretchr/testify/require" - - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/testutil/sims" - - "github.com/babylonchain/babylon/app" - - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/cosmos/cosmos-sdk/x/simulation" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" -) - -// Get flags every time the simulator is run -func init() { - GetSimulatorFlags() -} - -type StoreKeysPrefixes struct { - A storetypes.StoreKey - B storetypes.StoreKey - Prefixes [][]byte -} - -// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of -// an IAVLStore for faster simulation speed. -func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { - bapp.SetFauxMerkleMode() -} - -// interBlockCacheOpt returns a BaseApp option function that sets the persistent -// inter-block write-through cache. -func interBlockCacheOpt() func(*baseapp.BaseApp) { - return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) -} - -func TestFullAppSimulation(t *testing.T) { - config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") - if skip { - t.Skip("skipping application simulation") - } - require.NoError(t, err, "simulation setup failed") - - defer func() { - db.Close() - require.NoError(t, os.RemoveAll(dir)) - }() - - privSigner, err := app.SetupPrivSigner() - require.NoError(t, err) - babylon := app.NewBabylonApp(logger, db, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, fauxMerkleModeOpt) - require.Equal(t, "BabylonApp", babylon.Name()) - - // run randomized simulation - _, simParams, simErr := simulation.SimulateFromSeed( - t, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - - // export state and simParams before the simulation error is checked - err = sims.CheckExportSimulation(babylon, config, simParams) - require.NoError(t, err) - require.NoError(t, simErr) - - if config.Commit { - sims.PrintStats(db) - } -} - -func TestAppImportExport(t *testing.T) { - config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") - if skip { - t.Skip("skipping application import/export simulation") - } - require.NoError(t, err, "simulation setup failed") - - defer func() { - db.Close() - require.NoError(t, os.RemoveAll(dir)) - }() - - privSigner, err := app.SetupPrivSigner() - require.NoError(t, err) - babylon := app.NewBabylonApp(logger, db, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, fauxMerkleModeOpt) - require.Equal(t, "BabylonApp", babylon.Name()) - - // Run randomized simulation - _, simParams, simErr := simulation.SimulateFromSeed( - t, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - - // export state and simParams before the simulation error is checked - err = sims.CheckExportSimulation(babylon, config, simParams) - require.NoError(t, err) - require.NoError(t, simErr) - - if config.Commit { - sims.PrintStats(db) - } - - fmt.Printf("exporting genesis...\n") - - exported, err := babylon.ExportAppStateAndValidators(false, []string{}, []string{}) - require.NoError(t, err) - - fmt.Printf("importing genesis...\n") - - _, newDB, newDir, _, _, err := SetupSimulation("leveldb-app-sim-2", "Simulation-2") - require.NoError(t, err, "simulation setup failed") - - defer func() { - newDB.Close() - require.NoError(t, os.RemoveAll(newDir)) - }() - - newBabylon := app.NewBabylonApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, fauxMerkleModeOpt) - require.Equal(t, "BabylonApp", newBabylon.Name()) - - var genesisState app.GenesisState - err = json.Unmarshal(exported.AppState, &genesisState) - require.NoError(t, err) - - ctxA := babylon.NewContext(true, tmproto.Header{Height: babylon.LastBlockHeight()}) - ctxB := newBabylon.NewContext(true, tmproto.Header{Height: babylon.LastBlockHeight()}) - newBabylon.ModuleManager().InitGenesis(ctxB, babylon.AppCodec(), genesisState) - newBabylon.StoreConsensusParams(ctxB, exported.ConsensusParams) - - fmt.Printf("comparing stores...\n") - - storeKeysPrefixes := []StoreKeysPrefixes{ - {babylon.GetKey(authtypes.StoreKey), newBabylon.GetKey(authtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(stakingtypes.StoreKey), newBabylon.GetKey(stakingtypes.StoreKey), - [][]byte{ - stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, - stakingtypes.HistoricalInfoKey, - }}, // ordering may change but it doesn't matter - {babylon.GetKey(slashingtypes.StoreKey), newBabylon.GetKey(slashingtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(minttypes.StoreKey), newBabylon.GetKey(minttypes.StoreKey), [][]byte{}}, - {babylon.GetKey(distrtypes.StoreKey), newBabylon.GetKey(distrtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(banktypes.StoreKey), newBabylon.GetKey(banktypes.StoreKey), [][]byte{banktypes.BalancesPrefix}}, - {babylon.GetKey(paramtypes.StoreKey), newBabylon.GetKey(paramtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(govtypes.StoreKey), newBabylon.GetKey(govtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(evidencetypes.StoreKey), newBabylon.GetKey(evidencetypes.StoreKey), [][]byte{}}, - {babylon.GetKey(capabilitytypes.StoreKey), newBabylon.GetKey(capabilitytypes.StoreKey), [][]byte{}}, - {babylon.GetKey(authzkeeper.StoreKey), newBabylon.GetKey(authzkeeper.StoreKey), [][]byte{}}, - // TODO: add Babylon module StoreKey and prefix here - {babylon.GetKey(btccheckpointtypes.StoreKey), newBabylon.GetKey(btccheckpointtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(btclightclienttypes.StoreKey), newBabylon.GetKey(btclightclienttypes.StoreKey), [][]byte{}}, - {babylon.GetKey(checkpointingtypes.StoreKey), newBabylon.GetKey(checkpointingtypes.StoreKey), [][]byte{}}, - {babylon.GetKey(epochingtypes.StoreKey), newBabylon.GetKey(epochingtypes.StoreKey), - [][]byte{epochingtypes.SlashedVotingPowerKey, epochingtypes.VotingPowerKey}}, - } - - for _, skp := range storeKeysPrefixes { - storeA := ctxA.KVStore(skp.A) - storeB := ctxB.KVStore(skp.B) - - failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) - require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") - - fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) - require.Equal(t, len(failedKVAs), 0, sims.GetSimulationLog(skp.A.Name(), babylon.SimulationManager().StoreDecoders, failedKVAs, failedKVBs)) - } -} - -func TestAppSimulationAfterImport(t *testing.T) { - config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") - if skip { - t.Skip("skipping application simulation after import") - } - require.NoError(t, err, "simulation setup failed") - - defer func() { - db.Close() - require.NoError(t, os.RemoveAll(dir)) - }() - - privSigner, err := app.SetupPrivSigner() - require.NoError(t, err) - babylon := app.NewBabylonApp(logger, db, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, fauxMerkleModeOpt) - require.Equal(t, "BabylonApp", babylon.Name()) - - // Run randomized simulation - stopEarly, simParams, simErr := simulation.SimulateFromSeed( - t, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - - // export state and simParams before the simulation error is checked - err = sims.CheckExportSimulation(babylon, config, simParams) - require.NoError(t, err) - require.NoError(t, simErr) - - if config.Commit { - sims.PrintStats(db) - } - - if stopEarly { - fmt.Println("can't export or import a zero-validator genesis, exiting test...") - return - } - - fmt.Printf("exporting genesis...\n") - - exported, err := babylon.ExportAppStateAndValidators(true, []string{}, []string{}) - require.NoError(t, err) - - fmt.Printf("importing genesis...\n") - - _, newDB, newDir, _, _, err := SetupSimulation("leveldb-app-sim-2", "Simulation-2") - require.NoError(t, err, "simulation setup failed") - - defer func() { - newDB.Close() - require.NoError(t, os.RemoveAll(newDir)) - }() - - newBabylon := app.NewBabylonApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, fauxMerkleModeOpt) - require.Equal(t, "BabylonApp", newBabylon.Name()) - - newBabylon.InitChain(abci.RequestInitChain{ - AppStateBytes: exported.AppState, - }) - - _, _, err = simulation.SimulateFromSeed( - t, - os.Stdout, - newBabylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(newBabylon, newBabylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - require.NoError(t, err) -} - -// TODO: Make another test for the fuzzer itself, which just has noOp txs -// and doesn't depend on the application. -func TestAppStateDeterminism(t *testing.T) { - if !FlagEnabledValue { - t.Skip("skipping application simulation") - } - - config := NewConfigFromFlags() - config.InitialBlockHeight = 1 - config.ExportParamsPath = "" - config.OnOperation = false - config.AllInvariants = false - config.ChainID = simappparams.SimAppChainID - - numSeeds := 3 - numTimesToRunPerSeed := 5 - appHashList := make([]json.RawMessage, numTimesToRunPerSeed) - - for i := 0; i < numSeeds; i++ { - config.Seed = rand.Int63() - - for j := 0; j < numTimesToRunPerSeed; j++ { - var logger log.Logger - if FlagVerboseValue { - logger = log.TestingLogger() - } else { - logger = log.NewNopLogger() - } - - db := dbm.NewMemDB() - privSigner, err := app.SetupPrivSigner() - require.NoError(t, err) - babylon := app.NewBabylonApp(logger, db, nil, true, map[int64]bool{}, app.DefaultNodeHome, FlagPeriodValue, app.GetEncodingConfig(), privSigner, sims.EmptyAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, interBlockCacheOpt()) - - fmt.Printf( - "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", - config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, - ) - - _, _, err = simulation.SimulateFromSeed( - t, - os.Stdout, - babylon.BaseApp, - AppStateFn(babylon.AppCodec(), babylon.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - SimulationOperations(babylon, babylon.AppCodec(), config), - babylon.ModuleAccountAddrs(), - config, - babylon.AppCodec(), - ) - require.NoError(t, err) - - if config.Commit { - sims.PrintStats(db) - } - - appHash := babylon.LastCommitID().Hash - appHashList[j] = appHash - - if j != 0 { - require.Equal( - t, string(appHashList[0]), string(appHashList[j]), - "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, - ) - } - } - } -} diff --git a/simapp/state.go b/simapp/state.go deleted file mode 100644 index dec564272..000000000 --- a/simapp/state.go +++ /dev/null @@ -1,245 +0,0 @@ -package simapp - -import ( - "encoding/json" - "fmt" - "io" - "math/rand" - "os" - "time" - - sdkmath "cosmossdk.io/math" - simappparams "github.com/babylonchain/babylon/app/params" - - tmjson "github.com/cometbft/cometbft/libs/json" - tmtypes "github.com/cometbft/cometbft/types" - - "github.com/babylonchain/babylon/app" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// AppStateFn returns the initial application state using a genesis or the simulation parameters. -// It panics if the user provides files for both of them. -// If a file is not given for the genesis or the sim params, it creates a randomized one. -func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simtypes.AppStateFn { - return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config, - ) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) { - - if FlagGenesisTimeValue == 0 { - genesisTimestamp = simtypes.RandTimestamp(r) - } else { - genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0) - } - - chainID = config.ChainID - switch { - case config.ParamsFile != "" && config.GenesisFile != "": - panic("cannot provide both a genesis file and a params file") - case config.GenesisFile != "": - // override the default chain-id from simapp to set it later to the config - genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile) - - if FlagGenesisTimeValue == 0 { - // use genesis timestamp if no custom timestamp is provided (i.e no random timestamp) - genesisTimestamp = genesisDoc.GenesisTime - } - - appState = genesisDoc.AppState - chainID = genesisDoc.ChainID - simAccs = accounts - - case config.ParamsFile != "": - appParams := make(simtypes.AppParams) - bz, err := os.ReadFile(config.ParamsFile) - if err != nil { - panic(err) - } - - err = json.Unmarshal(bz, &appParams) - if err != nil { - panic(err) - } - appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) - default: - appParams := make(simtypes.AppParams) - appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) - } - - rawState := make(map[string]json.RawMessage) - err := json.Unmarshal(appState, &rawState) - if err != nil { - panic(err) - } - - stakingStateBz, ok := rawState[stakingtypes.ModuleName] - if !ok { - panic("staking genesis state is missing") - } - - stakingState := new(stakingtypes.GenesisState) - err = cdc.UnmarshalJSON(stakingStateBz, stakingState) - if err != nil { - panic(err) - } - // compute not bonded balance - notBondedTokens := sdk.ZeroInt() - for _, val := range stakingState.Validators { - if val.Status != stakingtypes.Unbonded { - continue - } - notBondedTokens = notBondedTokens.Add(val.GetTokens()) - } - notBondedCoins := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens) - // edit bank state to make it have the not bonded pool tokens - bankStateBz, ok := rawState[banktypes.ModuleName] - // TODO(fdymylja/jonathan): should we panic in this case - if !ok { - panic("bank genesis state is missing") - } - bankState := new(banktypes.GenesisState) - err = cdc.UnmarshalJSON(bankStateBz, bankState) - if err != nil { - panic(err) - } - - stakingAddr := authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String() - var found bool - for _, balance := range bankState.Balances { - if balance.Address == stakingAddr { - found = true - break - } - } - if !found { - bankState.Balances = append(bankState.Balances, banktypes.Balance{ - Address: stakingAddr, - Coins: sdk.NewCoins(notBondedCoins), - }) - } - - // change appState back - rawState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingState) - rawState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankState) - - // replace appstate - appState, err = json.Marshal(rawState) - if err != nil { - panic(err) - } - return appState, simAccs, chainID, genesisTimestamp - } -} - -// AppStateRandomizedFn creates calls each module's GenesisState generator function -// and creates the simulation params -func AppStateRandomizedFn( - simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONCodec, - accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams, -) (json.RawMessage, []simtypes.Account) { - numAccs := int64(len(accs)) - genesisState := app.NewDefaultGenesisState(cdc) - - // generate a random amount of initial stake coins and a random initial - // number of bonded accounts - var initialStake, numInitiallyBonded int64 - appParams.GetOrGenerate( - cdc, simappparams.StakePerAccount, &initialStake, r, - func(r *rand.Rand) { initialStake = r.Int63n(1e12) }, - ) - appParams.GetOrGenerate( - cdc, simappparams.InitiallyBondedValidators, &numInitiallyBonded, r, - func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) }, - ) - - if numInitiallyBonded > numAccs { - numInitiallyBonded = numAccs - } - - fmt.Printf( - `Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "%d", - initially_bonded_validators: "%d" -} -`, initialStake, numInitiallyBonded, - ) - - simState := &module.SimulationState{ - AppParams: appParams, - Cdc: cdc, - Rand: r, - GenState: genesisState, - Accounts: accs, - InitialStake: sdkmath.NewInt(initialStake), - NumBonded: numInitiallyBonded, - GenTimestamp: genesisTimestamp, - } - - simManager.GenerateGenesisStates(simState) - - appState, err := json.Marshal(genesisState) - if err != nil { - panic(err) - } - - return appState, accs -} - -// AppStateFromGenesisFileFn util function to generate the genesis AppState -// from a genesis.json file. -func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) { - bytes, err := os.ReadFile(genesisFile) - if err != nil { - panic(err) - } - - var genesis tmtypes.GenesisDoc - // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc - err = tmjson.Unmarshal(bytes, &genesis) - if err != nil { - panic(err) - } - - var appState app.GenesisState - err = json.Unmarshal(genesis.AppState, &appState) - if err != nil { - panic(err) - } - - var authGenesis authtypes.GenesisState - if appState[authtypes.ModuleName] != nil { - cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis) - } - - newAccs := make([]simtypes.Account, len(authGenesis.Accounts)) - for i, acc := range authGenesis.Accounts { - // Pick a random private key, since we don't know the actual key - // This should be fine as it's only used for mock Tendermint validators - // and these keys are never actually used to sign by mock Tendermint. - privkeySeed := make([]byte, 15) - if _, err := r.Read(privkeySeed); err != nil { - panic(err) - } - - privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed) - - a, ok := acc.GetCachedValue().(authtypes.AccountI) - if !ok { - panic("expected account") - } - - // create simulator accounts - simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()} - newAccs[i] = simAcc - } - - return genesis, newAccs -} diff --git a/simapp/utils.go b/simapp/utils.go deleted file mode 100644 index 6efac1755..000000000 --- a/simapp/utils.go +++ /dev/null @@ -1,87 +0,0 @@ -package simapp - -import ( - "encoding/json" - "os" - - "github.com/babylonchain/babylon/app" - simappparams "github.com/babylonchain/babylon/app/params" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/codec" - - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// SetupSimulation creates the config, db (levelDB), temporary directory and logger for the simulation tests. -// If `FlagEnabledValue` is false it skips the current test. -// Returns error on an invalid db intantiation or temp dir creation. -// NOTE: this function is identical to https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/simapp/utils.go. -// The reason of migrating it here is that it uses the modules' flags initialised in `init()`. Otherwise, -// if using `sdksimapp.SetupSimulation`, then `sdksimapp.FlagEnabledValue` and `sdksimapp.FlagVerboseValue` -// will be used, rather than those in `config.go` of our module. The other functions under -// https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/simapp/utils.go do not access flags and thus can be reused safely. -func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, log.Logger, bool, error) { - if !FlagEnabledValue { - return simtypes.Config{}, nil, "", nil, true, nil - } - - config := NewConfigFromFlags() - config.ChainID = simappparams.SimAppChainID - - var logger log.Logger - if FlagVerboseValue { - logger = log.TestingLogger() - } else { - logger = log.NewNopLogger() - } - - dir, err := os.MkdirTemp("", dirPrefix) - if err != nil { - return simtypes.Config{}, nil, "", nil, false, err - } - - db, err := dbm.NewDB(dbName, dbm.BackendType(config.DBBackend), dir) - if err != nil { - return simtypes.Config{}, nil, "", nil, false, err - } - - return config, db, dir, logger, false, nil -} - -// SimulationOperations retrieves the simulation params from the provided file path -// and returns all the modules weighted operations -// NOTE: the code is same as https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/simapp/utils.go#L50-L73, -// except that this function modifies the default weights given in Cosmos SDK. -// Specifically, Babylon does not want unwrapped message types in the staking module. -func SimulationOperations(app app.App, cdc codec.JSONCodec, config simtypes.Config) []simtypes.WeightedOperation { - simState := module.SimulationState{ - AppParams: make(simtypes.AppParams), - Cdc: cdc, - } - - if config.ParamsFile != "" { - bz, err := os.ReadFile(config.ParamsFile) - if err != nil { - panic(err) - } - - err = json.Unmarshal(bz, &simState.AppParams) - if err != nil { - panic(err) - } - } - // get weighted operations from all modules, except for the staking module whose messages will be rejected by Babylon - sm := app.SimulationManager() - appWOps := make([]simtypes.WeightedOperation, 0, len(sm.Modules)) - for _, module := range sm.Modules { - if _, ok := module.(staking.AppModule); ok { - continue - } - appWOps = append(appWOps, module.WeightedOperations(simState)...) - } - - return appWOps -} diff --git a/test/e2e/README.md b/test/e2e/README.md index fb9f1751d..d29a6539a 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -9,13 +9,13 @@ 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 +Wasm contract located in `bytecode/babylon_contract.wasm` is compiled from most recent commit `main` branch - https://github.com/babylonchain/babylon-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 -containers are removed before running the tests again: `docker containers rm -f $(docker containers ls -a -q)`. +containers are removed before running the tests again: `docker container rm -f $(docker container ls -a -q)`. Additionally, Docker networks do not get auto-removed. Therefore, you can manually remove them by running `docker network prune`. diff --git a/test/e2e/btc_staking_e2e_test.go b/test/e2e/btc_staking_e2e_test.go new file mode 100644 index 000000000..ffd19b59e --- /dev/null +++ b/test/e2e/btc_staking_e2e_test.go @@ -0,0 +1,511 @@ +package e2e + +import ( + "math" + "math/rand" + "time" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/babylonchain/babylon/crypto/eots" + "github.com/babylonchain/babylon/test/e2e/configurer" + "github.com/babylonchain/babylon/test/e2e/initialization" + "github.com/babylonchain/babylon/test/e2e/util" + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + ftypes "github.com/babylonchain/babylon/x/finality/types" + itypes "github.com/babylonchain/babylon/x/incentive/types" +) + +var ( + r = rand.New(rand.NewSource(time.Now().Unix())) + net = &chaincfg.SimNetParams + // finality provider + fpBTCSK, _, _ = datagen.GenRandomBTCKeyPair(r) + fp *bstypes.FinalityProvider + // BTC delegation + delBTCSK, delBTCPK, _ = datagen.GenRandomBTCKeyPair(r) + // covenant + covenantSKs, _, covenantQuorum = bstypes.DefaultCovenantCommittee() + + stakingValue = int64(2 * 10e8) +) + +type BTCStakingTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *BTCStakingTestSuite) SetupSuite() { + s.T().Log("setting up e2e integration test suite...") + var err error + + // The e2e test flow is as follows: + // + // 1. Configure 1 chain with some validator nodes + // 2. Execute various e2e tests + s.configurer, err = configurer.NewBTCStakingConfigurer(s.T(), true) + s.NoError(err) + err = s.configurer.ConfigureChains() + s.NoError(err) + err = s.configurer.RunSetup() + s.NoError(err) +} + +func (s *BTCStakingTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +// TestCreateFinalityProviderAndDelegation is an end-to-end test for +// user story 1: user creates finality provider and BTC delegation +func (s *BTCStakingTestSuite) Test1CreateFinalityProviderAndDelegation() { + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(1) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + /* + create a random finality provider on Babylon + */ + // NOTE: we use the node's secret key as Babylon secret key for the finality provider + fp, err = datagen.GenRandomFinalityProviderWithBTCBabylonSKs(r, fpBTCSK, nonValidatorNode.SecretKey) + s.NoError(err) + nonValidatorNode.CreateFinalityProvider(fp.BabylonPk, fp.BtcPk, fp.Pop, fp.Description.Moniker, fp.Description.Identity, fp.Description.Website, fp.Description.SecurityContact, fp.Description.Details, fp.Commission) + + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + + // query the existence of finality provider and assert equivalence + actualFps := nonValidatorNode.QueryFinalityProviders() + s.Len(actualFps, 1) + s.Equal(util.Cdc.MustMarshal(fp), util.Cdc.MustMarshal(actualFps[0])) + + /* + create a random BTC delegation under this finality provider + */ + // BTC staking params, BTC delegation key pairs and PoP + params := nonValidatorNode.QueryBTCStakingParams() + + // minimal required unbonding time + unbondingTime := uint16(initialization.BabylonBtcFinalizationPeriod) + 1 + + // get covenant BTC PKs + covenantBTCPKs := []*btcec.PublicKey{} + for _, covenantPK := range params.CovenantPks { + covenantBTCPKs = append(covenantBTCPKs, covenantPK.MustToBTCPK()) + } + // NOTE: we use the node's secret key as Babylon secret key for the BTC delegation + delBabylonSK := nonValidatorNode.SecretKey + pop, err := bstypes.NewPoP(delBabylonSK, delBTCSK) + s.NoError(err) + // generate staking tx and slashing tx + stakingTimeBlocks := uint16(math.MaxUint16) + testStakingInfo := datagen.GenBTCStakingSlashingInfo( + r, + s.T(), + net, + delBTCSK, + []*btcec.PublicKey{fp.BtcPk.MustToBTCPK()}, + covenantBTCPKs, + covenantQuorum, + stakingTimeBlocks, + stakingValue, + params.SlashingAddress, + params.SlashingRate, + unbondingTime, + ) + + stakingMsgTx := testStakingInfo.StakingTx + stakingTxHash := stakingMsgTx.TxHash().String() + stakingSlashingPathInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() + s.NoError(err) + + // generate proper delegator sig + delegatorSig, err := testStakingInfo.SlashingTx.Sign( + stakingMsgTx, + datagen.StakingOutIdx, + stakingSlashingPathInfo.GetPkScriptPath(), + delBTCSK, + ) + s.NoError(err) + + // submit staking tx to Bitcoin and get inclusion proof + currentBtcTip, err := nonValidatorNode.QueryTip() + s.NoError(err) + blockWithStakingTx := datagen.CreateBlockWithTransaction(r, currentBtcTip.Header.ToBlockHeader(), stakingMsgTx) + nonValidatorNode.InsertHeader(&blockWithStakingTx.HeaderBytes) + // make block k-deep + for i := 0; i < initialization.BabylonBtcConfirmationPeriod; i++ { + nonValidatorNode.InsertNewEmptyBtcHeader(r) + } + stakingTxInfo := btcctypes.NewTransactionInfoFromSpvProof(blockWithStakingTx.SpvProof) + + // generate BTC undelegation stuff + stkTxHash := testStakingInfo.StakingTx.TxHash() + unbondingValue := stakingValue - datagen.UnbondingTxFee // TODO: parameterise fee + testUnbondingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + s.T(), + net, + delBTCSK, + []*btcec.PublicKey{fp.BtcPk.MustToBTCPK()}, + covenantBTCPKs, + covenantQuorum, + wire.NewOutPoint(&stkTxHash, datagen.StakingOutIdx), + stakingTimeBlocks, + unbondingValue, + params.SlashingAddress, + params.SlashingRate, + unbondingTime, + ) + delUnbondingSlashingSig, err := testUnbondingInfo.GenDelSlashingTxSig(delBTCSK) + s.NoError(err) + + // submit the message for creating BTC delegation + nonValidatorNode.CreateBTCDelegation( + delBabylonSK.PubKey().(*secp256k1.PubKey), + bbn.NewBIP340PubKeyFromBTCPK(delBTCPK), + pop, + stakingTxInfo, + fp.BtcPk, + stakingTimeBlocks, + btcutil.Amount(stakingValue), + testStakingInfo.SlashingTx, + delegatorSig, + testUnbondingInfo.UnbondingTx, + testUnbondingInfo.SlashingTx, + uint16(unbondingTime), + btcutil.Amount(unbondingValue), + delUnbondingSlashingSig, + ) + + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + nonValidatorNode.WaitForNextBlock() + + pendingDelSet := nonValidatorNode.QueryFinalityProviderDelegations(fp.BtcPk.MarshalHex()) + s.Len(pendingDelSet, 1) + pendingDels := pendingDelSet[0] + s.Len(pendingDels.Dels, 1) + s.Equal(delBTCPK.SerializeCompressed()[1:], pendingDels.Dels[0].BtcPk.MustToBTCPK().SerializeCompressed()[1:]) + s.Len(pendingDels.Dels[0].CovenantSigs, 0) + + // check delegation + delegation := nonValidatorNode.QueryBtcDelegation(stakingTxHash) + s.NotNil(delegation) +} + +// Test2SubmitCovenantSignature is an end-to-end test for user +// story 2: covenant approves the BTC delegation +func (s *BTCStakingTestSuite) Test2SubmitCovenantSignature() { + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(1) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // get last BTC delegation + pendingDelsSet := nonValidatorNode.QueryFinalityProviderDelegations(fp.BtcPk.MarshalHex()) + s.Len(pendingDelsSet, 1) + pendingDels := pendingDelsSet[0] + s.Len(pendingDels.Dels, 1) + pendingDel := pendingDels.Dels[0] + s.Len(pendingDel.CovenantSigs, 0) + + slashingTx := pendingDel.SlashingTx + stakingTx := pendingDel.StakingTx + stakingMsgTx, err := bbn.NewBTCTxFromBytes(stakingTx) + s.NoError(err) + stakingTxHash := stakingMsgTx.TxHash().String() + + params := nonValidatorNode.QueryBTCStakingParams() + + fpBTCPKs, err := bbn.NewBTCPKsFromBIP340PKs(pendingDel.FpBtcPkList) + s.NoError(err) + + stakingInfo, err := pendingDel.GetStakingInfo(params, net) + s.NoError(err) + + stakingSlashingPathInfo, err := stakingInfo.SlashingPathSpendInfo() + s.NoError(err) + + /* + generate and insert new covenant signature, in order to activate the BTC delegation + */ + // covenant signatures on slashing tx + covenantSlashingSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + fpBTCPKs, + stakingMsgTx, + stakingSlashingPathInfo.GetPkScriptPath(), + slashingTx, + ) + s.NoError(err) + + // cov Schnorr sigs on unbonding signature + unbondingPathInfo, err := stakingInfo.UnbondingPathSpendInfo() + s.NoError(err) + unbondingTx, err := bbn.NewBTCTxFromBytes(pendingDel.BtcUndelegation.UnbondingTx) + s.NoError(err) + + covUnbondingSigs, err := datagen.GenCovenantUnbondingSigs( + covenantSKs, + stakingMsgTx, + pendingDel.StakingOutputIdx, + unbondingPathInfo.GetPkScriptPath(), + unbondingTx, + ) + s.NoError(err) + + unbondingInfo, err := pendingDel.GetUnbondingInfo(params, net) + s.NoError(err) + unbondingSlashingPathInfo, err := unbondingInfo.SlashingPathSpendInfo() + s.NoError(err) + covenantUnbondingSlashingSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + fpBTCPKs, + unbondingTx, + unbondingSlashingPathInfo.GetPkScriptPath(), + pendingDel.BtcUndelegation.SlashingTx, + ) + s.NoError(err) + + for i := 0; i < int(covenantQuorum); i++ { + nonValidatorNode.AddCovenantSigs( + covenantSlashingSigs[i].CovPk, + stakingTxHash, + covenantSlashingSigs[i].AdaptorSigs, + bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), + covenantUnbondingSlashingSigs[i].AdaptorSigs, + ) + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + } + + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + nonValidatorNode.WaitForNextBlock() + + // ensure the BTC delegation has covenant sigs now + activeDelsSet := nonValidatorNode.QueryFinalityProviderDelegations(fp.BtcPk.MarshalHex()) + s.Len(activeDelsSet, 1) + activeDels := activeDelsSet[0] + s.Len(activeDels.Dels, 1) + activeDel := activeDels.Dels[0] + s.True(activeDel.HasCovenantQuorums(covenantQuorum)) + + // wait for a block so that above txs take effect and the voting power table + // is updated in the next block's BeginBlock + nonValidatorNode.WaitForNextBlock() + + // ensure BTC staking is activated + activatedHeight := nonValidatorNode.QueryActivatedHeight() + s.Positive(activatedHeight) + // ensure finality provider has voting power at activated height + currentBtcTip, err := nonValidatorNode.QueryTip() + s.NoError(err) + activeFps := nonValidatorNode.QueryActiveFinalityProvidersAtHeight(activatedHeight) + s.Len(activeFps, 1) + s.Equal(activeFps[0].VotingPower, activeDels.VotingPower(currentBtcTip.Height, initialization.BabylonBtcFinalizationPeriod, params.CovenantQuorum)) + s.Equal(activeFps[0].VotingPower, activeDel.VotingPower(currentBtcTip.Height, initialization.BabylonBtcFinalizationPeriod, params.CovenantQuorum)) +} + +// Test2CommitPublicRandomnessAndSubmitFinalitySignature is an end-to-end +// test for user story 3: finality provider commits public randomness and submits +// finality signature, such that blocks can be finalised. +func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignature() { + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(1) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // get activated height + activatedHeight := nonValidatorNode.QueryActivatedHeight() + s.Positive(activatedHeight) + + /* + commit a number of public randomness since activatedHeight + */ + // commit public randomness list + srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, fpBTCSK, activatedHeight, 100) + s.NoError(err) + nonValidatorNode.CommitPubRandList( + msgCommitPubRandList.FpBtcPk, + msgCommitPubRandList.StartHeight, + msgCommitPubRandList.PubRandList, + msgCommitPubRandList.Sig, + ) + + // ensure public randomness list is eventually committed + nonValidatorNode.WaitForNextBlock() + var pubRandMap map[uint64]*bbn.SchnorrPubRand + s.Eventually(func() bool { + pubRandMap = nonValidatorNode.QueryListPublicRandomness(fp.BtcPk) + return len(pubRandMap) > 0 + }, time.Minute, time.Second*5) + s.Equal(pubRandMap[activatedHeight].MustMarshal(), msgCommitPubRandList.PubRandList[0].MustMarshal()) + + // no reward gauge for finality provider and delegation yet + fpBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes()) + _, err = nonValidatorNode.QueryRewardGauge(fpBabylonAddr) + s.Error(err) + delBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes()) + _, err = nonValidatorNode.QueryRewardGauge(delBabylonAddr) + s.Error(err) + + /* + submit finality signature + */ + // get block to vote + blockToVote, err := nonValidatorNode.QueryBlock(int64(activatedHeight)) + s.NoError(err) + appHash := blockToVote.AppHash + + msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), appHash...) + // generate EOTS signature + sig, err := eots.Sign(fpBTCSK, srList[0], msgToSign) + s.NoError(err) + eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) + // submit finality signature + nonValidatorNode.AddFinalitySig(fp.BtcPk, activatedHeight, appHash, eotsSig) + + // ensure vote is eventually cast + nonValidatorNode.WaitForNextBlock() + var votes []bbn.BIP340PubKey + s.Eventually(func() bool { + votes = nonValidatorNode.QueryVotesAtHeight(activatedHeight) + return len(votes) > 0 + }, time.Minute, time.Second*5) + s.Equal(1, len(votes)) + s.Equal(votes[0].MarshalHex(), fp.BtcPk.MarshalHex()) + // once the vote is cast, ensure block is finalised + finalizedBlock := nonValidatorNode.QueryIndexedBlock(activatedHeight) + s.NotEmpty(finalizedBlock) + s.Equal(appHash.Bytes(), finalizedBlock.AppHash) + finalizedBlocks := nonValidatorNode.QueryListBlocks(ftypes.QueriedBlockStatus_FINALIZED) + s.NotEmpty(finalizedBlocks) + s.Equal(appHash.Bytes(), finalizedBlocks[0].AppHash) + + // ensure finality provider has received rewards after the block is finalised + fpRewardGauges, err := nonValidatorNode.QueryRewardGauge(fpBabylonAddr) + s.NoError(err) + fpRewardGauge, ok := fpRewardGauges[itypes.FinalityProviderType.String()] + s.True(ok) + s.True(fpRewardGauge.Coins.IsAllPositive()) + // ensure BTC delegation has received rewards after the block is finalised + btcDelRewardGauges, err := nonValidatorNode.QueryRewardGauge(delBabylonAddr) + s.NoError(err) + btcDelRewardGauge, ok := btcDelRewardGauges[itypes.BTCDelegationType.String()] + s.True(ok) + s.True(btcDelRewardGauge.Coins.IsAllPositive()) +} + +func (s *BTCStakingTestSuite) Test4WithdrawReward() { + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // finality provider balance before withdraw + fpBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes()) + delBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes()) + fpBalance, err := nonValidatorNode.QueryBalances(fpBabylonAddr.String()) + s.NoError(err) + // finality provider reward gauge should not be fully withdrawn + fpRgs, err := nonValidatorNode.QueryRewardGauge(fpBabylonAddr) + s.NoError(err) + fpRg := fpRgs[itypes.FinalityProviderType.String()] + s.T().Logf("finality provider's withdrawable reward before withdrawing: %s", fpRg.GetWithdrawableCoins().String()) + s.False(fpRg.IsFullyWithdrawn()) + + // withdraw finality provider reward + nonValidatorNode.WithdrawReward(itypes.FinalityProviderType.String(), initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + + // balance after withdrawing finality provider reward + fpBalance2, err := nonValidatorNode.QueryBalances(fpBabylonAddr.String()) + s.NoError(err) + s.T().Logf("fpBalance2: %s; fpBalance: %s", fpBalance2.String(), fpBalance.String()) + s.True(fpBalance2.IsAllGT(fpBalance)) + // finality provider reward gauge should be fully withdrawn now + fpRgs2, err := nonValidatorNode.QueryRewardGauge(fpBabylonAddr) + s.NoError(err) + fpRg2 := fpRgs2[itypes.FinalityProviderType.String()] + s.T().Logf("finality provider's withdrawable reward after withdrawing: %s", fpRg2.GetWithdrawableCoins().String()) + s.True(fpRg2.IsFullyWithdrawn()) + + // BTC delegation balance before withdraw + btcDelBalance, err := nonValidatorNode.QueryBalances(delBabylonAddr.String()) + s.NoError(err) + // BTC delegation reward gauge should not be fully withdrawn + btcDelRgs, err := nonValidatorNode.QueryRewardGauge(delBabylonAddr) + s.NoError(err) + btcDelRg := btcDelRgs[itypes.BTCDelegationType.String()] + s.T().Logf("BTC delegation's withdrawable reward before withdrawing: %s", btcDelRg.GetWithdrawableCoins().String()) + s.False(btcDelRg.IsFullyWithdrawn()) + + // withdraw BTC delegation reward + nonValidatorNode.WithdrawReward(itypes.BTCDelegationType.String(), initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + + // balance after withdrawing BTC delegation reward + btcDelBalance2, err := nonValidatorNode.QueryBalances(delBabylonAddr.String()) + s.NoError(err) + s.T().Logf("btcDelBalance2: %s; btcDelBalance: %s", btcDelBalance2.String(), btcDelBalance.String()) + s.True(btcDelBalance2.IsAllGT(btcDelBalance)) + // BTC delegation reward gauge should be fully withdrawn now + btcDelRgs2, err := nonValidatorNode.QueryRewardGauge(delBabylonAddr) + s.NoError(err) + btcDelRg2 := btcDelRgs2[itypes.BTCDelegationType.String()] + s.T().Logf("BTC delegation's withdrawable reward after withdrawing: %s", btcDelRg2.GetWithdrawableCoins().String()) + s.True(btcDelRg2.IsFullyWithdrawn()) +} + +// Test5SubmitStakerUnbonding is an end-to-end test for user unbonding +func (s *BTCStakingTestSuite) Test5SubmitStakerUnbonding() { + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(1) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + + activeDelsSet := nonValidatorNode.QueryFinalityProviderDelegations(fp.BtcPk.MarshalHex()) + s.Len(activeDelsSet, 1) + activeDels := activeDelsSet[0] + s.Len(activeDels.Dels, 1) + activeDel := activeDels.Dels[0] + s.NotNil(activeDel.CovenantSigs) + + // staking tx hash + stakingMsgTx, err := bbn.NewBTCTxFromBytes(activeDel.StakingTx) + s.NoError(err) + stakingTxHash := stakingMsgTx.TxHash() + + // delegator signs unbonding tx + params := nonValidatorNode.QueryBTCStakingParams() + delUnbondingSig, err := activeDel.SignUnbondingTx(params, net, delBTCSK) + s.NoError(err) + + // submit the message for creating BTC undelegation + nonValidatorNode.BTCUndelegate(&stakingTxHash, delUnbondingSig) + // wait for a block so that above txs take effect + nonValidatorNode.WaitForNextBlock() + + // Wait for unbonded delegations to be created + var unbondedDels []*bstypes.BTCDelegation + s.Eventually(func() bool { + unbondedDels = nonValidatorNode.QueryUnbondedDelegations() + return len(unbondedDels) > 0 + }, time.Minute, time.Second*2) + s.Len(unbondedDels, 1) + s.Equal(stakingTxHash, unbondedDels[0].MustGetStakingTxHash()) +} diff --git a/test/e2e/btc_timestamping_e2e_test.go b/test/e2e/btc_timestamping_e2e_test.go new file mode 100644 index 000000000..0f7fb1804 --- /dev/null +++ b/test/e2e/btc_timestamping_e2e_test.go @@ -0,0 +1,351 @@ +package e2e + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "math/rand" + "strconv" + "time" + + "github.com/babylonchain/babylon/test/e2e/configurer" + "github.com/babylonchain/babylon/test/e2e/initialization" + bbn "github.com/babylonchain/babylon/types" + ct "github.com/babylonchain/babylon/x/checkpointing/types" + itypes "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" +) + +type BTCTimestampingTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *BTCTimestampingTestSuite) SetupSuite() { + s.T().Log("setting up e2e integration test suite...") + var ( + err error + ) + + // The e2e test flow is as follows: + // + // 1. Configure two chains - chan A and chain B. + // * For each chain, set up several validator nodes + // * Initialize configs and genesis for all them. + // 2. Start both networks. + // 3. Run IBC relayer between the two chains. + // 4. Execute various e2e tests, including IBC + s.configurer, err = configurer.NewBTCTimestampingConfigurer(s.T(), true) + + s.Require().NoError(err) + + err = s.configurer.ConfigureChains() + s.Require().NoError(err) + + err = s.configurer.RunSetup() + s.Require().NoError(err) +} + +func (s *BTCTimestampingTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +// Most simple test, just checking that two chains are up and connected through +// ibc +func (s *BTCTimestampingTestSuite) Test1ConnectIbc() { + chainA := s.configurer.GetChainConfig(0) + chainB := s.configurer.GetChainConfig(1) + _, err := chainA.GetDefaultNode() + s.NoError(err) + _, err = chainB.GetDefaultNode() + s.NoError(err) +} + +func (s *BTCTimestampingTestSuite) Test2BTCBaseHeader() { + hardcodedHeader, _ := bbn.NewBTCHeaderBytesFromHex("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a45068653ffff7f2002000000") + hardcodedHeaderHeight := uint64(0) + + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + baseHeader, err := nonValidatorNode.QueryBtcBaseHeader() + s.NoError(err) + s.True(baseHeader.Hash.Eq(hardcodedHeader.Hash())) + s.Equal(hardcodedHeaderHeight, baseHeader.Height) +} + +func (s *BTCTimestampingTestSuite) Test3SendTx() { + r := rand.New(rand.NewSource(time.Now().Unix())) + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + tip1, err := nonValidatorNode.QueryTip() + s.NoError(err) + + nonValidatorNode.InsertNewEmptyBtcHeader(r) + + tip2, err := nonValidatorNode.QueryTip() + s.NoError(err) + + s.Equal(tip1.Height+1, tip2.Height) + + // check that light client properly updates its state + tip1Depth, err := nonValidatorNode.QueryHeaderDepth(tip1.Hash.MarshalHex()) + s.NoError(err) + s.Equal(tip1Depth, uint64(1)) + + tip2Depth, err := nonValidatorNode.QueryHeaderDepth(tip2.Hash.MarshalHex()) + s.NoError(err) + // tip should have 0 depth + s.Equal(tip2Depth, uint64(0)) +} + +func (s *BTCTimestampingTestSuite) Test4IbcCheckpointing() { + chainA := s.configurer.GetChainConfig(0) + chainA.WaitUntilHeight(35) + + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // Query checkpoint chain info for opposing chain + chainsInfo, err := nonValidatorNode.QueryChainsInfo([]string{initialization.ChainBID}) + s.NoError(err) + s.Equal(chainsInfo[0].ChainId, initialization.ChainBID) + + // Finalize epoch 1, 2, 3, as first headers of opposing chain are in epoch 3 + var ( + startEpochNum uint64 = 1 + endEpochNum uint64 = 3 + ) + + // submitter/reporter address should not have any rewards yet + submitterReporterAddr := sdk.MustAccAddressFromBech32(nonValidatorNode.PublicAddress) + _, err = nonValidatorNode.QueryRewardGauge(submitterReporterAddr) + s.Error(err) + + nonValidatorNode.FinalizeSealedEpochs(startEpochNum, endEpochNum) + + endEpoch, err := nonValidatorNode.QueryRawCheckpoint(endEpochNum) + s.NoError(err) + s.Equal(endEpoch.Status, ct.Finalized) + + // Wait for a some time to ensure that the checkpoint is included in the chain + time.Sleep(20 * time.Second) + // Wait for next block + nonValidatorNode.WaitForNextBlock() + + // Check we have epoch info for opposing chain and some basic assertions + epochChainsInfo, err := nonValidatorNode.QueryEpochChainsInfo(endEpochNum, []string{initialization.ChainBID}) + s.NoError(err) + s.Equal(epochChainsInfo[0].ChainId, initialization.ChainBID) + s.Equal(epochChainsInfo[0].LatestHeader.BabylonEpoch, endEpochNum) + + // Check we have finalized epoch info for opposing chain and some basic assertions + finalizedChainsInfo, err := nonValidatorNode.QueryFinalizedChainsInfo([]string{initialization.ChainBID}) + s.NoError(err) + + // TODO Add more assertion here. Maybe check proofs ? + s.Equal(finalizedChainsInfo[0].FinalizedChainInfo.ChainId, initialization.ChainBID) + s.Equal(finalizedChainsInfo[0].EpochInfo.EpochNumber, endEpochNum) + + currEpoch, err := nonValidatorNode.QueryCurrentEpoch() + s.NoError(err) + + heightAtEndedEpoch, err := nonValidatorNode.QueryLightClientHeightEpochEnd(currEpoch - 1) + s.NoError(err) + + if heightAtEndedEpoch == 0 { + // we can only assert, that btc lc height is larger than 0. + s.FailNow(fmt.Sprintf("Light client height should be > 0 on epoch %d", currEpoch-1)) + } + + // ensure balance has increased after finalising some epochs + rewardGauges, err := nonValidatorNode.QueryRewardGauge(submitterReporterAddr) + s.NoError(err) + submitterRewardGauge, ok := rewardGauges[itypes.SubmitterType.String()] + s.True(ok) + s.True(submitterRewardGauge.Coins.IsAllPositive()) + reporterRewardGauge, ok := rewardGauges[itypes.ReporterType.String()] + s.True(ok) + s.True(reporterRewardGauge.Coins.IsAllPositive()) + + chainB := s.configurer.GetChainConfig(1) + _, err = chainB.GetDefaultNode() + s.NoError(err) +} + +func (s *BTCTimestampingTestSuite) Test5WithdrawReward() { + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // NOTE: nonValidatorNode.PublicAddress is the address associated with key name `val` + // and is both the submitter and reporter + submitterReporterAddr := sdk.MustAccAddressFromBech32(nonValidatorNode.PublicAddress) + + // balance before withdraw + balance, err := nonValidatorNode.QueryBalances(submitterReporterAddr.String()) + s.NoError(err) + // submitter/reporter reward gauges before withdraw should not be fully withdrawn + rgs, err := nonValidatorNode.QueryRewardGauge(submitterReporterAddr) + s.NoError(err) + submitterRg, reporterRg := rgs[itypes.SubmitterType.String()], rgs[itypes.ReporterType.String()] + s.T().Logf("submitter witdhrawable reward: %s, reporter witdhrawable reward: %s before withdrawing", submitterRg.GetWithdrawableCoins().String(), reporterRg.GetWithdrawableCoins().String()) + s.False(submitterRg.IsFullyWithdrawn()) + s.False(reporterRg.IsFullyWithdrawn()) + + // withdraw submitter reward + nonValidatorNode.WithdrawReward(itypes.SubmitterType.String(), initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + + // balance after withdrawing submitter reward + balance2, err := nonValidatorNode.QueryBalances(submitterReporterAddr.String()) + s.NoError(err) + s.T().Logf("balance2: %s; balance: %s", balance2.String(), balance.String()) + s.True(balance2.IsAllGT(balance)) + + // submitter reward gauge should be fully withdrawn + rgs2, err := nonValidatorNode.QueryRewardGauge(submitterReporterAddr) + s.NoError(err) + submitterRg2 := rgs2[itypes.SubmitterType.String()] + s.T().Logf("submitter withdrawable reward: %s after withdrawing", submitterRg2.GetWithdrawableCoins().String()) + s.True(rgs2[itypes.SubmitterType.String()].IsFullyWithdrawn()) + + // withdraw reporter reward + nonValidatorNode.WithdrawReward(itypes.ReporterType.String(), initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + + // balance after withdrawing reporter reward + balance3, err := nonValidatorNode.QueryBalances(submitterReporterAddr.String()) + s.NoError(err) + s.T().Logf("balance3: %s; balance2: %s", balance3.String(), balance2.String()) + s.True(balance3.IsAllGT(balance2)) + + // reporter reward gauge should be fully withdrawn + rgs3, err := nonValidatorNode.QueryRewardGauge(submitterReporterAddr) + s.NoError(err) + reporterRg3 := rgs3[itypes.SubmitterType.String()] + s.T().Logf("reporter withdrawable reward: %s after withdrawing", reporterRg3.GetWithdrawableCoins().String()) + s.True(rgs3[itypes.ReporterType.String()].IsFullyWithdrawn()) +} + +func (s *BTCTimestampingTestSuite) Test6Wasm() { + contractPath := "/bytecode/storage_contract.wasm" + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // store the wasm code + latestWasmId := int(nonValidatorNode.QueryLatestWasmCodeID()) + nonValidatorNode.StoreWasmCode(contractPath, initialization.ValidatorWalletName) + s.Eventually(func() bool { + newLatestWasmId := int(nonValidatorNode.QueryLatestWasmCodeID()) + if latestWasmId+1 > newLatestWasmId { + return false + } + latestWasmId = newLatestWasmId + return true + }, time.Second*20, time.Second) + + // instantiate the wasm contract + var contracts []string + nonValidatorNode.InstantiateWasmContract( + strconv.Itoa(latestWasmId), + `{}`, + initialization.ValidatorWalletName, + ) + s.Eventually(func() bool { + contracts, err = nonValidatorNode.QueryContractsFromId(latestWasmId) + return err == nil && len(contracts) == 1 + }, time.Second*10, time.Second) + contractAddr := contracts[0] + + // execute contract + 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) + + // the data is eventually included in the contract + queryMsg := fmt.Sprintf(`{"check_data": { "data_hash": "%s" } }`, dataHashHex) + var queryResult map[string]interface{} + s.Eventually(func() bool { + queryResult, err = nonValidatorNode.QueryWasmSmartObject(contractAddr, queryMsg) + return err == nil + }, time.Second*10, time.Second) + + finalized := queryResult["finalized"].(bool) + latestFinalizedEpoch := int(queryResult["latest_finalized_epoch"].(float64)) + saveEpoch := int(queryResult["save_epoch"].(float64)) + + s.False(finalized) + // in previous test we already finalized epoch 3 + s.Equal(3, latestFinalizedEpoch) + // data is not finalized yet, so save epoch should be strictly greater than latest finalized epoch + s.Greater(saveEpoch, latestFinalizedEpoch) +} + +func (s *BTCTimestampingTestSuite) Test7InterceptFeeCollector() { + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + + // ensure incentive module account has positive balance + incentiveModuleAddr, err := nonValidatorNode.QueryModuleAddress(itypes.ModuleName) + s.NoError(err) + incentiveBalance, err := nonValidatorNode.QueryBalances(incentiveModuleAddr.String()) + s.NoError(err) + s.NotEmpty(incentiveBalance) + s.T().Logf("incentive module account's balance: %s", incentiveBalance.String()) + s.True(incentiveBalance.IsAllPositive()) + + // ensure BTC staking gauge at the current height is eventually non-empty + // NOTE: sometimes incentive module's BeginBlock is not triggered yet. If this + // happens, we might need to wait for some time. + curHeight, err := nonValidatorNode.QueryCurrentHeight() + s.NoError(err) + s.Eventually(func() bool { + btcStakingGauge, err := nonValidatorNode.QueryBTCStakingGauge(uint64(curHeight)) + if err != nil { + return false + } + s.T().Logf("BTC staking gauge at current height %d: %s", curHeight, btcStakingGauge.String()) + return len(btcStakingGauge.Coins) >= 1 && btcStakingGauge.Coins[0].Amount.IsPositive() + }, time.Second*10, time.Second) + + // ensure BTC timestamping gauge at the current epoch is non-empty + curEpoch, err := nonValidatorNode.QueryCurrentEpoch() + s.NoError(err) + // at the 1st block of an epoch, the gauge does not exist since incentive's BeginBlock + // at this block accumulates rewards for BTC timestamping gauge for the previous block + // need to wait for a block to ensure the gauge is created + var btcTimestampingGauge *itypes.Gauge + s.Eventually(func() bool { + btcTimestampingGauge, err = nonValidatorNode.QueryBTCTimestampingGauge(curEpoch) + if err != nil { + return false + } + s.T().Logf("BTC timestamping gauge at current epoch %d: %s", curEpoch, btcTimestampingGauge.String()) + return !btcTimestampingGauge.Coins.Empty() + }, time.Second*10, time.Second) + + // wait for 1 block to see if BTC timestamp gauge has accumulated + nonValidatorNode.WaitForNextBlock() + btcTimestampingGauge2, err := nonValidatorNode.QueryBTCTimestampingGauge(curEpoch) + s.NoError(err) + s.T().Logf("BTC timestamping gauge after a block at current epoch %d: %s", curEpoch, btcTimestampingGauge2.String()) + s.NotEmpty(btcTimestampingGauge2.Coins) + s.True(btcTimestampingGauge2.Coins.IsAllGTE(btcTimestampingGauge.Coins)) + + // after 1 block, incentive's balance has to be accumulated + incentiveBalance2, err := nonValidatorNode.QueryBalances(incentiveModuleAddr.String()) + s.NoError(err) + s.T().Logf("incentive module account's balance after a block: %s", incentiveBalance2.String()) + s.True(incentiveBalance2.IsAllGTE(incentiveBalance)) +} diff --git a/test/e2e/btc_timestamping_phase2_hermes_test.go b/test/e2e/btc_timestamping_phase2_hermes_test.go new file mode 100644 index 000000000..0029da76e --- /dev/null +++ b/test/e2e/btc_timestamping_phase2_hermes_test.go @@ -0,0 +1,127 @@ +package e2e + +import ( + "time" + + "github.com/babylonchain/babylon/test/e2e/configurer" + "github.com/babylonchain/babylon/test/e2e/initialization" + ct "github.com/babylonchain/babylon/x/checkpointing/types" + zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" + "github.com/cosmos/cosmos-sdk/types/query" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/stretchr/testify/suite" +) + +type BTCTimestampingPhase2HermesTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *BTCTimestampingPhase2HermesTestSuite) SetupSuite() { + s.T().Log("setting up phase 2 integration test suite...") + var ( + err error + ) + + // The e2e test flow is as follows: + // + // 1. Configure two chains - chain A and chain B. + // * For each chain, set up several validator nodes + // * Initialize configs and genesis for all them. + // 2. Start both networks. + // 3. Store and instantiate babylon contract on chain B. + // 3. Execute various e2e tests, excluding IBC + s.configurer, err = configurer.NewBTCTimestampingPhase2Configurer(s.T(), true) + + s.Require().NoError(err) + + err = s.configurer.ConfigureChains() + s.Require().NoError(err) + + err = s.configurer.RunSetup() + s.Require().NoError(err) +} + +func (s *BTCTimestampingPhase2HermesTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +func (s *BTCTimestampingPhase2HermesTestSuite) Test1IbcCheckpointingPhase2Hermes() { + chainA := s.configurer.GetChainConfig(0) + + babylonNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + czNode, err := s.configurer.GetChainConfig(1).GetNodeAtIndex(2) + s.NoError(err) + + // Validate channel state and kind (Babylon side) + babylonChannelsResp, err := babylonNode.QueryIBCChannels() + s.NoError(err) + s.Len(babylonChannelsResp.Channels, 1) + babylonChannel := babylonChannelsResp.Channels[0] + // channel has to be open and ordered + s.Equal(channeltypes.OPEN, babylonChannel.State) + s.Equal(channeltypes.ORDERED, babylonChannel.Ordering) + // the counterparty has to be the Babylon smart contract + s.Contains(babylonChannel.Counterparty.PortId, "wasm.") + + // Validate channel state (CZ side) + czChannelsResp, err := czNode.QueryIBCChannels() + s.NoError(err) + s.Len(czChannelsResp.Channels, 2) // TODO: why 2 channels? + czChannel := czChannelsResp.Channels[0] + s.Equal(channeltypes.OPEN, czChannel.State) + s.Equal(channeltypes.ORDERED, czChannel.Ordering) + s.Equal(zctypes.PortID, czChannel.Counterparty.PortId) + + // Query checkpoint chain info for the consumer chain + listHeaderResp, err := babylonNode.QueryListHeaders(initialization.ChainBID, &query.PageRequest{Limit: 1}) + s.NoError(err) + startEpochNum := listHeaderResp.Headers[0].BabylonEpoch + endEpochNum := startEpochNum + 2 + + // wait until epoch endEpochNum + // so that there will be endEpochNum - startEpochNum + 1 = 3 + // BTC timestamps in Babylon contract + chainA.WaitUntilHeight(int64(endEpochNum*10 + 5)) + babylonNode.FinalizeSealedEpochs(1, endEpochNum) + + // ensure endEpochNum has been finalised + endEpoch, err := babylonNode.QueryRawCheckpoint(endEpochNum) + s.NoError(err) + s.Equal(endEpoch.Status, ct.Finalized) + + // there should be 3 IBC packets sent (with sequence number 1, 2, 3). + // Thus, the next sequence number will eventually be 4 + s.Eventually(func() bool { + nextSequenceSendResp, err := babylonNode.QueryNextSequenceSend(babylonChannel.ChannelId, babylonChannel.PortId) + if err != nil { + return false + } + s.T().Logf("next sequence send at ZoneConcierge is %d", nextSequenceSendResp.NextSequenceSend) + return nextSequenceSendResp.NextSequenceSend >= endEpochNum-startEpochNum+1+1 + }, time.Minute, time.Second*2) + + // ensure the next receive sequence number of Babylon contract is also 3 + s.Eventually(func() bool { + nextSequenceRecv, err := czNode.QueryNextSequenceReceive(babylonChannel.Counterparty.ChannelId, babylonChannel.Counterparty.PortId) + if err != nil { + return false + } + s.T().Logf("next sequence receive at Babylon contract is %d", nextSequenceRecv.NextSequenceReceive) + return nextSequenceRecv.NextSequenceReceive >= endEpochNum-startEpochNum+1+1 + }, time.Minute, time.Second*2) + + // Ensure the IBC packet acknowledgements (on chain B) are there + lastSequence := endEpochNum + for seq := uint64(1); seq < lastSequence; seq++ { + var seqResp *channeltypes.QueryPacketAcknowledgementResponse + s.Eventually(func() bool { + seqResp, err = czNode.QueryPacketAcknowledgement(czChannel.ChannelId, czChannel.PortId, seq) + s.T().Logf("acknowledgement resp of IBC packet #%d: %v, err: %v", seq, seqResp, err) + return err == nil + }, time.Minute, time.Second*2) + } +} diff --git a/test/e2e/btc_timestamping_phase2_rly_test.go b/test/e2e/btc_timestamping_phase2_rly_test.go new file mode 100644 index 000000000..8bc1e5352 --- /dev/null +++ b/test/e2e/btc_timestamping_phase2_rly_test.go @@ -0,0 +1,148 @@ +package e2e + +import ( + "time" + + "github.com/babylonchain/babylon/test/e2e/configurer" + "github.com/babylonchain/babylon/test/e2e/initialization" + ct "github.com/babylonchain/babylon/x/checkpointing/types" + "github.com/cosmos/cosmos-sdk/types/query" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/stretchr/testify/suite" +) + +type BTCTimestampingPhase2RlyTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *BTCTimestampingPhase2RlyTestSuite) SetupSuite() { + s.T().Log("setting up phase 2 go relayer integration test suite...") + var ( + err error + ) + + // The e2e test flow is as follows: + // + // 1. Configure two chains - chain A and chain B. + // * For each chain, set up several validator nodes + // * Initialize configs and genesis for all them. + // 2. Start both networks. + // 3. Store and instantiate babylon contract on chain B. + // 3. Execute various e2e tests, excluding IBC + s.configurer, err = configurer.NewBTCTimestampingPhase2RlyConfigurer(s.T(), true) + + s.Require().NoError(err) + + err = s.configurer.ConfigureChains() + s.Require().NoError(err) + + err = s.configurer.RunSetup() + s.Require().NoError(err) +} + +func (s *BTCTimestampingPhase2RlyTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +func (s *BTCTimestampingPhase2RlyTestSuite) Test1IbcCheckpointingPhase2Rly() { + chainA := s.configurer.GetChainConfig(0) + + babylonNode, err := chainA.GetNodeAtIndex(2) + s.NoError(err) + czNode, err := s.configurer.GetChainConfig(1).GetNodeAtIndex(2) + s.NoError(err) + + // Validate channel state and kind (Babylon side) + // Wait until the channel (Babylon side) is open + var babylonChannel *channeltypes.IdentifiedChannel + s.Eventually(func() bool { + babylonChannelsResp, err := babylonNode.QueryIBCChannels() + if err != nil { + return false + } + if len(babylonChannelsResp.Channels) != 1 { + return false + } + // channel has to be open and ordered + babylonChannel = babylonChannelsResp.Channels[0] + if babylonChannel.State != channeltypes.OPEN { + return false + } + s.Equal(channeltypes.ORDERED, babylonChannel.Ordering) + // the counterparty has to be the Babylon smart contract + s.Contains(babylonChannel.Counterparty.PortId, "wasm.") + return true + }, time.Minute, time.Second*2) + + // Wait until the channel (CZ side) is open + var czChannel *channeltypes.IdentifiedChannel + s.Eventually(func() bool { + czChannelsResp, err := czNode.QueryIBCChannels() + if err != nil { + return false + } + if len(czChannelsResp.Channels) != 1 { + return false + } + czChannel = czChannelsResp.Channels[0] + if czChannel.State != channeltypes.OPEN { + return false + } + s.Equal(channeltypes.ORDERED, czChannel.Ordering) + s.Equal(babylonChannel.PortId, czChannel.Counterparty.PortId) + return true + }, time.Minute, time.Second*2) + + // Query checkpoint chain info for the consumer chain + listHeaderResp, err := babylonNode.QueryListHeaders(initialization.ChainBID, &query.PageRequest{Limit: 1}) + s.NoError(err) + startEpochNum := listHeaderResp.Headers[0].BabylonEpoch + endEpochNum := startEpochNum + 2 + + // wait until epoch endEpochNum + // so that there will be endEpochNum - startEpochNum + 1 = 3 + // BTC timestamps in Babylon contract + chainA.WaitUntilHeight(int64(endEpochNum*10 + 5)) + babylonNode.FinalizeSealedEpochs(1, endEpochNum) + + // ensure endEpochNum has been finalised + endEpoch, err := babylonNode.QueryRawCheckpoint(endEpochNum) + s.NoError(err) + s.Equal(endEpoch.Status, ct.Finalized) + + // there should be 3 IBC packets sent (with sequence number 1, 2, 3). + // Thus, the next sequence number will eventually be 4 + s.Eventually(func() bool { + nextSequenceSendResp, err := babylonNode.QueryNextSequenceSend(babylonChannel.ChannelId, babylonChannel.PortId) + if err != nil { + return false + } + s.T().Logf("next sequence send at ZoneConcierge is %d", nextSequenceSendResp.NextSequenceSend) + return nextSequenceSendResp.NextSequenceSend >= endEpochNum-startEpochNum+1+1 + }, time.Minute, time.Second*2) + + // ensure the next receive sequence number of Babylon contract is also 3 + var nextSequenceRecv *channeltypes.QueryNextSequenceReceiveResponse + s.Eventually(func() bool { + nextSequenceRecv, err = czNode.QueryNextSequenceReceive(babylonChannel.Counterparty.ChannelId, babylonChannel.Counterparty.PortId) + if err != nil { + return false + } + s.T().Logf("next sequence receive at Babylon contract is %d", nextSequenceRecv.NextSequenceReceive) + return nextSequenceRecv.NextSequenceReceive >= endEpochNum-startEpochNum+1+1 + }, time.Minute, time.Second*2) + + // Ensure the IBC packet acknowledgements (on chain B) are there + nextSequence := nextSequenceRecv.NextSequenceReceive + for seq := uint64(1); seq < nextSequence; seq++ { + var seqResp *channeltypes.QueryPacketAcknowledgementResponse + s.Eventually(func() bool { + seqResp, err = czNode.QueryPacketAcknowledgement(czChannel.ChannelId, czChannel.PortId, seq) + s.T().Logf("acknowledgement resp of IBC packet #%d: %v, err: %v", seq, seqResp, err) + return err == nil + }, time.Minute, time.Second*2) + } +} diff --git a/test/e2e/bytecode/README.md b/test/e2e/bytecode/README.md new file mode 100644 index 000000000..e0926f743 --- /dev/null +++ b/test/e2e/bytecode/README.md @@ -0,0 +1 @@ +Contract built from https://github.com/babylonchain/babylon-contract/tree/main/contracts diff --git a/test/e2e/bytecode/babylon_contract.wasm b/test/e2e/bytecode/babylon_contract.wasm new file mode 100644 index 000000000..f43c2f174 Binary files /dev/null and b/test/e2e/bytecode/babylon_contract.wasm differ diff --git a/test/e2e/bytecode/storage_contract.wasm b/test/e2e/bytecode/storage_contract.wasm index 229a10d89..af97c5294 100644 Binary files a/test/e2e/bytecode/storage_contract.wasm and b/test/e2e/bytecode/storage_contract.wasm differ diff --git a/test/e2e/bytecode/version.txt b/test/e2e/bytecode/version.txt new file mode 100644 index 000000000..3e53cef7d --- /dev/null +++ b/test/e2e/bytecode/version.txt @@ -0,0 +1 @@ +535d854ed239020edd3e93cc202a7c1bcbd1a162 diff --git a/test/e2e/configurer/base.go b/test/e2e/configurer/base.go index f27ca0227..6744fff94 100644 --- a/test/e2e/configurer/base.go +++ b/test/e2e/configurer/base.go @@ -8,16 +8,17 @@ import ( "os" "path" "path/filepath" + "strconv" "testing" "time" - "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/util" - zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" + "github.com/babylonchain/babylon/types" + types2 "github.com/babylonchain/babylon/x/btccheckpoint/types" + "github.com/stretchr/testify/require" ) // baseConfigurer is the base implementation for the @@ -43,7 +44,9 @@ func (bc *baseConfigurer) ClearResources() error { } for _, chainConfig := range bc.chainConfigs { - os.RemoveAll(chainConfig.DataDir) + if err := os.RemoveAll(chainConfig.DataDir); err != nil { + return err + } } return nil } @@ -71,11 +74,88 @@ func (bc *baseConfigurer) runValidators(chainConfig *chain.Config) error { return nil } -func (bc *baseConfigurer) RunIBC() error { +func (bc *baseConfigurer) InstantiateBabylonContract() error { + // Store the contract on the second chain (B) + chainConfig := bc.chainConfigs[1] + contractPath := "/bytecode/babylon_contract.wasm" + nonValidatorNode, err := chainConfig.GetNodeAtIndex(2) + if err != nil { + bc.t.Logf("error getting non-validator node: %v", err) + return err + } + nonValidatorNode.StoreWasmCode(contractPath, initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + nonValidatorNode.WaitForNextBlock() + + latestWasmId := int(nonValidatorNode.QueryLatestWasmCodeID()) + + // Instantiate the contract + initMsg := fmt.Sprintf(`{ "network": %q, "babylon_tag": %q, "btc_confirmation_depth": %d, "checkpoint_finalization_timeout": %d, "notify_cosmos_zone": %s }`, + types.BtcRegtest, + types2.DefaultCheckpointTag, + 1, + 2, + "false", + ) + nonValidatorNode.InstantiateWasmContract( + strconv.Itoa(latestWasmId), + initMsg, + initialization.ValidatorWalletName, + ) + nonValidatorNode.WaitForNextBlock() + contracts, err := nonValidatorNode.QueryContractsFromId(1) + if err != nil { + bc.t.Logf("error querying contracts from id: %v", err) + return err + } + require.Len(bc.t, contracts, 1, "Wrong number of contracts for the counter") + contractAddr := contracts[0] + + // Set the contract address in the IBC chain config port id. + chainConfig.IBCConfig.PortID = fmt.Sprintf("wasm.%s", contractAddr) + + return nil +} + +func (bc *baseConfigurer) RunHermesRelayerIBC() error { + // Run a relayer between every possible pair of chains. + for i := 0; i < len(bc.chainConfigs); i++ { + for j := i + 1; j < len(bc.chainConfigs); j++ { + if err := bc.runHermesIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + return err + } + if err := bc.createBabylonPhase2Channel(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + return err + } + } + } + return nil +} + +func (bc *baseConfigurer) RunCosmosRelayerIBC() error { + // Run a relayer between every possible pair of chains. + for i := 0; i < len(bc.chainConfigs); i++ { + for j := i + 1; j < len(bc.chainConfigs); j++ { + if err := bc.runCosmosIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + return err + } + //if err := bc.createBabylonPhase2Channel(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + // return err + //} + } + } + // Launches a relayer between chain A (babylond) and chain B (wasmd) + return nil +} + +func (bc *baseConfigurer) RunIBCTransferChannel() error { // Run a relayer between every possible pair of chains. for i := 0; i < len(bc.chainConfigs); i++ { for j := i + 1; j < len(bc.chainConfigs); j++ { - if err := bc.runIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + if err := bc.runHermesIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + return err + } + if err := bc.createIBCTransferChannel(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { return err } } @@ -83,7 +163,7 @@ func (bc *baseConfigurer) RunIBC() error { return nil } -func (bc *baseConfigurer) runIBCRelayer(chainConfigA *chain.Config, chainConfigB *chain.Config) error { +func (bc *baseConfigurer) runHermesIBCRelayer(chainConfigA *chain.Config, chainConfigB *chain.Config) error { bc.t.Log("starting Hermes relayer container...") tmpDir, err := os.MkdirTemp("", "bbn-e2e-testnet-hermes-") @@ -160,19 +240,70 @@ func (bc *baseConfigurer) runIBCRelayer(chainConfigA *chain.Config, chainConfigB // XXX: Give time to both networks to start, otherwise we might see gRPC // transport errors. - time.Sleep(10 * time.Second) + time.Sleep(3 * time.Second) - // create the client, connection and channel between the two babylon chains - return bc.connectIBCChains(chainConfigA, chainConfigB) + return nil } -func (bc *baseConfigurer) connectIBCChains(chainA *chain.Config, chainB *chain.Config) error { +func (bc *baseConfigurer) runCosmosIBCRelayer(chainConfigA *chain.Config, chainConfigB *chain.Config) error { + bc.t.Log("Starting Cosmos relayer container...") + + tmpDir, err := os.MkdirTemp("", "bbn-e2e-testnet-cosmos-") + if err != nil { + return err + } + + rlyCfgPath := path.Join(tmpDir, "rly") + + if err := os.MkdirAll(rlyCfgPath, 0o755); err != nil { + return err + } + + _, err = util.CopyFile( + filepath.Join("./scripts/", "rly_bootstrap.sh"), + filepath.Join(rlyCfgPath, "rly_bootstrap.sh"), + ) + if err != nil { + return err + } + + // we are using non validator nodes as validator are constantly sending bls + // transactions, which makes relayer operations failing + relayerNodeA := chainConfigA.NodeConfigs[2] + relayerNodeB := chainConfigB.NodeConfigs[2] + + rlyResource, err := bc.containerManager.RunRlyResource( + chainConfigA.Id, + relayerNodeA.Name, + relayerNodeA.Mnemonic, + chainConfigA.IBCConfig.PortID, + chainConfigB.Id, + relayerNodeB.Name, + relayerNodeB.Mnemonic, + chainConfigB.IBCConfig.PortID, + rlyCfgPath) + if err != nil { + return err + } + + // Wait for the relayer to connect to the chains + bc.t.Logf("waiting for Cosmos relayer setup...") + time.Sleep(30 * time.Second) + + bc.t.Logf("started Cosmos relayer container: %s", rlyResource.Container.ID) + + return nil +} + +func (bc *baseConfigurer) createBabylonPhase2Channel(chainA *chain.Config, chainB *chain.Config) error { bc.t.Logf("connecting %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) + require.Equal(bc.t, chainA.IBCConfig.Order, chainB.IBCConfig.Order) + require.Equal(bc.t, chainA.IBCConfig.Version, chainB.IBCConfig.Version) 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 + "--a-port", chainA.IBCConfig.PortID, "--b-port", chainB.IBCConfig.PortID, // port + "--order", chainA.IBCConfig.Order.String(), + "--channel-version", chainA.IBCConfig.Version, "--new-client-connection", "--yes", } _, _, err := bc.containerManager.ExecHermesCmd(bc.t, cmd, "SUCCESS") @@ -180,6 +311,20 @@ func (bc *baseConfigurer) connectIBCChains(chainA *chain.Config, chainB *chain.C return err } bc.t.Logf("connected %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) + bc.t.Logf("chainA's IBC config: %v", chainA.IBCConfig) + bc.t.Logf("chainB's IBC config: %v", chainB.IBCConfig) + return nil +} + +func (bc *baseConfigurer) createIBCTransferChannel(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", "transfer", "--b-port", "transfer", "--new-client-connection", "--yes"} + bc.t.Log(cmd) + _, _, err := bc.containerManager.ExecHermesCmd(bc.t, cmd, "SUCCESS") + if err != nil { + return err + } + bc.t.Logf("connected %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) return nil } diff --git a/test/e2e/configurer/chain/chain.go b/test/e2e/configurer/chain/chain.go index f08f8f575..4d5e1ec03 100644 --- a/test/e2e/configurer/chain/chain.go +++ b/test/e2e/configurer/chain/chain.go @@ -2,6 +2,7 @@ package chain import ( "fmt" + ibctesting "github.com/cosmos/ibc-go/v8/testing" "testing" "time" @@ -26,6 +27,7 @@ type Config struct { LatestProposalNumber int LatestLockNumber int NodeConfigs []*NodeConfig + IBCConfig *ibctesting.ChannelConfig LatestCodeId int @@ -43,13 +45,14 @@ const ( waitUntilrepeatMax = 60 ) -func New(t *testing.T, containerManager *containers.Manager, id string, initValidatorConfigs []*initialization.NodeConfig) *Config { +func New(t *testing.T, containerManager *containers.Manager, id string, initValidatorConfigs []*initialization.NodeConfig, ibcConfig *ibctesting.ChannelConfig) *Config { numVal := float32(len(initValidatorConfigs)) return &Config{ ChainMeta: initialization.ChainMeta{ Id: id, }, ValidatorInitConfigs: initValidatorConfigs, + IBCConfig: ibcConfig, VotingPeriod: config.PropDepositBlocks + numVal*config.PropVoteBlocks + config.PropBufferBlocks, ExpeditedVotingPeriod: config.PropDepositBlocks + numVal*config.PropVoteBlocks + config.PropBufferBlocks - 2, t: t, @@ -61,7 +64,7 @@ func New(t *testing.T, containerManager *containers.Manager, id string, initVali func (c *Config) CreateNode(initNode *initialization.Node) *NodeConfig { nodeConfig := &NodeConfig{ Node: *initNode, - chainId: c.Id, + chainId: c.ChainMeta.Id, containerManager: c.containerManager, t: c.t, } @@ -149,7 +152,7 @@ func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) { // GetDefaultNode returns the default node of the chain. // The default node is the first one created. Returns error if no -// ndoes created. +// nodes created. func (c *Config) GetDefaultNode() (*NodeConfig, error) { return c.GetNodeAtIndex(defaultNodeIndex) } diff --git a/test/e2e/configurer/chain/commands.go b/test/e2e/configurer/chain/commands.go index 945f85a20..94335bc60 100644 --- a/test/e2e/configurer/chain/commands.go +++ b/test/e2e/configurer/chain/commands.go @@ -5,30 +5,39 @@ import ( "encoding/json" "fmt" "math/rand" + "regexp" "strings" "time" - "github.com/cosmos/cosmos-sdk/types/bech32" - - sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" - - btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - cttypes "github.com/babylonchain/babylon/x/checkpointing/types" - txformat "github.com/babylonchain/babylon/btctxformatter" - bbn "github.com/babylonchain/babylon/types" - "github.com/babylonchain/babylon/test/e2e/initialization" "github.com/babylonchain/babylon/test/e2e/util" "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" blc "github.com/babylonchain/babylon/x/btclightclient/types" - + cttypes "github.com/babylonchain/babylon/x/checkpointing/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" ) -// TODO for now all commands are not used and left here as an example +func (n *NodeConfig) GetWallet(walletName string) string { + n.LogActionF("retrieving wallet %s", walletName) + cmd := []string{"babylond", "keys", "show", walletName, "--keyring-backend=test"} + outBuf, _, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") + require.NoError(n.t, err) + re := regexp.MustCompile("bbn(.{38})") + walletAddr := fmt.Sprintf("%s\n", re.FindString(outBuf.String())) + walletAddr = strings.TrimSuffix(walletAddr, "\n") + n.LogActionF("wallet %s found, waller address - %s", walletName, walletAddr) + return walletAddr +} + // QueryParams extracts the params for a given subspace and key. This is done generically via json to avoid having to // specify the QueryParamResponse type (which may not exist for all params). +// TODO for now all commands are not used and left here as an example func (n *NodeConfig) QueryParams(subspace, key string, result any) { cmd := []string{"babylond", "query", "params", "subspace", subspace, key, "--output=json"} @@ -39,6 +48,16 @@ func (n *NodeConfig) QueryParams(subspace, key string, result any) { require.NoError(n.t, err) } +func (n *NodeConfig) SendIBCTransfer(from, recipient, memo string, token sdk.Coin) { + n.LogActionF("IBC sending %s from %s to %s. memo: %s", token.Amount.String(), from, recipient, memo) + + cmd := []string{"babylond", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, token.String(), fmt.Sprintf("--from=%s", from), "--memo", memo} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + + n.LogActionF("successfully submitted sent IBC transfer") +} + func (n *NodeConfig) FailIBCTransfer(from, recipient, amount string) { n.LogActionF("IBC sending %s from %s to %s", amount, from, recipient) @@ -60,7 +79,7 @@ func (n *NodeConfig) BankSend(amount string, sendAddress string, receiveAddress func (n *NodeConfig) SendHeaderHex(headerHex string) { n.LogActionF("btclightclient sending header %s", headerHex) - cmd := []string{"babylond", "tx", "btclightclient", "insert-header", headerHex, "--from=val", "--gas=500000"} + cmd := []string{"babylond", "tx", "btclightclient", "insert-headers", headerHex, "--from=val", "--gas=500000"} _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully inserted header %s", headerHex) @@ -124,10 +143,10 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) { currentBtcTip, err := n.QueryTip() require.NoError(n.t, err) - _, c, err := bech32.DecodeAndConvert(n.PublicAddress) + _, submitterAddr, err := bech32.DecodeAndConvert(n.PublicAddress) require.NoError(n.t, err) - btcCheckpoint, err := cttypes.FromRawCkptToBTCCkpt(checkpoint.Ckpt, c) + btcCheckpoint, err := cttypes.FromRawCkptToBTCCkpt(checkpoint.Ckpt, submitterAddr) require.NoError(n.t, err) babylonTagBytes, err := hex.DecodeString(initialization.BabylonOpReturnTag) @@ -140,8 +159,10 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) { ) require.NoError(n.t, err) - opReturn1 := datagen.CreateBlockWithTransaction(r, currentBtcTip.Header.ToBlockHeader(), p1) - opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), p2) + tx1 := datagen.CreatOpReturnTransaction(r, p1) + opReturn1 := datagen.CreateBlockWithTransaction(r, currentBtcTip.Header.ToBlockHeader(), tx1) + tx2 := datagen.CreatOpReturnTransaction(r, p2) + opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) n.InsertHeader(&opReturn1.HeaderBytes) n.InsertHeader(&opReturn2.HeaderBytes) @@ -193,3 +214,13 @@ func (n *NodeConfig) WasmExecute(contract, execMsg, from string) { require.NoError(n.t, err) n.LogActionF("successfully executed") } + +// WithdrawReward will withdraw the rewards of the address associated with the tx signer `from` +func (n *NodeConfig) WithdrawReward(sType, from string) { + n.LogActionF("withdraw rewards of type %s for tx signer %s", sType, from) + cmd := []string{"babylond", "tx", "incentive", "withdraw-reward", sType, 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 withdrawn") +} diff --git a/test/e2e/configurer/chain/commands_btcstaking.go b/test/e2e/configurer/chain/commands_btcstaking.go new file mode 100644 index 000000000..7397af143 --- /dev/null +++ b/test/e2e/configurer/chain/commands_btcstaking.go @@ -0,0 +1,213 @@ +package chain + +import ( + "encoding/hex" + "strconv" + "strings" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/stretchr/testify/require" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" +) + +func (n *NodeConfig) CreateFinalityProvider(babylonPK *secp256k1.PubKey, btcPK *bbn.BIP340PubKey, pop *bstypes.ProofOfPossession, moniker, identity, website, securityContract, details string, commission *sdkmath.LegacyDec) { + n.LogActionF("creating finality provider") + + // get babylon PK hex + babylonPKBytes, err := babylonPK.Marshal() + require.NoError(n.t, err) + babylonPKHex := hex.EncodeToString(babylonPKBytes) + // get BTC PK hex + btcPKHex := btcPK.MarshalHex() + // get pop hex + popHex, err := pop.ToHexStr() + require.NoError(n.t, err) + + cmd := []string{ + "babylond", "tx", "btcstaking", "create-finality-provider", babylonPKHex, btcPKHex, popHex, "--from=val", "--moniker", moniker, "--identity", identity, "--website", website, "--security-contact", securityContract, "--details", details, "--commission-rate", commission.String(), + } + _, _, err = n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully created finality provider") +} + +func (n *NodeConfig) CreateBTCDelegation( + babylonPK *secp256k1.PubKey, + btcPk *bbn.BIP340PubKey, + pop *bstypes.ProofOfPossession, + stakingTxInfo *btcctypes.TransactionInfo, + fpPK *bbn.BIP340PubKey, + stakingTimeBlocks uint16, + stakingValue btcutil.Amount, + slashingTx *bstypes.BTCSlashingTx, + delegatorSig *bbn.BIP340Signature, + unbondingTx *wire.MsgTx, + unbondingSlashingTx *bstypes.BTCSlashingTx, + unbondingTime uint16, + unbondingValue btcutil.Amount, + delUnbondingSlashingSig *bbn.BIP340Signature, +) { + n.LogActionF("creating BTC delegation") + + // get babylon PK hex + babylonPKBytes, err := babylonPK.Marshal() + require.NoError(n.t, err) + babylonPKHex := hex.EncodeToString(babylonPKBytes) + + btcPkHex := btcPk.MarshalHex() + + // get pop hex + popHex, err := pop.ToHexStr() + require.NoError(n.t, err) + + // get staking tx info hex + stakingTxInfoHex, err := stakingTxInfo.ToHexStr() + require.NoError(n.t, err) + + fpPKHex := fpPK.MarshalHex() + + stakingTimeString := sdkmath.NewUint(uint64(stakingTimeBlocks)).String() + stakingValueString := sdkmath.NewInt(int64(stakingValue)).String() + + // get slashing tx hex + slashingTxHex := slashingTx.ToHexStr() + // get delegator sig hex + delegatorSigHex := delegatorSig.ToHexStr() + + // on-demand unbonding related + unbondingTxBytes, err := bbn.SerializeBTCTx(unbondingTx) + require.NoError(n.t, err) + unbondingTxHex := hex.EncodeToString(unbondingTxBytes) + unbondingSlashingTxHex := unbondingSlashingTx.ToHexStr() + unbondingTimeStr := sdkmath.NewUint(uint64(unbondingTime)).String() + unbondingValueStr := sdkmath.NewInt(int64(unbondingValue)).String() + delUnbondingSlashingSigHex := delUnbondingSlashingSig.ToHexStr() + + cmd := []string{"babylond", "tx", "btcstaking", "create-btc-delegation", babylonPKHex, btcPkHex, popHex, stakingTxInfoHex, fpPKHex, stakingTimeString, stakingValueString, slashingTxHex, delegatorSigHex, unbondingTxHex, unbondingSlashingTxHex, unbondingTimeStr, unbondingValueStr, delUnbondingSlashingSigHex, "--from=val"} + _, _, err = n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully created BTC delegation") +} + +func (n *NodeConfig) AddCovenantSigs(covPK *bbn.BIP340PubKey, stakingTxHash string, slashingSigs [][]byte, unbondingSig *bbn.BIP340Signature, unbondingSlashingSigs [][]byte) { + n.LogActionF("adding covenant signature") + + covPKHex := covPK.MarshalHex() + + cmd := []string{"babylond", "tx", "btcstaking", "add-covenant-sigs", covPKHex, stakingTxHash} + + // slashing signatures + var slashingSigStrList []string + for _, sig := range slashingSigs { + slashingSigStrList = append(slashingSigStrList, hex.EncodeToString(sig)) + } + slashingSigStr := strings.Join(slashingSigStrList, ",") + cmd = append(cmd, slashingSigStr) + + // on-demand unbonding stuff + cmd = append(cmd, unbondingSig.ToHexStr()) + var unbondingSlashingSigStrList []string + for _, sig := range unbondingSlashingSigs { + unbondingSlashingSigStrList = append(unbondingSlashingSigStrList, hex.EncodeToString(sig)) + } + unbondingSlashingSigStr := strings.Join(unbondingSlashingSigStrList, ",") + cmd = append(cmd, unbondingSlashingSigStr) + + // used key + cmd = append(cmd, "--from=val") + // gas + cmd = append(cmd, "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3") + + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully added covenant signatures") +} + +func (n *NodeConfig) CommitPubRandList(fpBTCPK *bbn.BIP340PubKey, startHeight uint64, pubRandList []bbn.SchnorrPubRand, sig *bbn.BIP340Signature) { + n.LogActionF("committing public randomness list") + + cmd := []string{"babylond", "tx", "finality", "commit-pubrand-list"} + + // add finality provider BTC PK to cmd + fpBTCPKHex := fpBTCPK.MarshalHex() + cmd = append(cmd, fpBTCPKHex) + + // add start height to cmd + startHeightStr := strconv.FormatUint(startHeight, 10) + cmd = append(cmd, startHeightStr) + + // add each pubrand to cmd + for _, pr := range pubRandList { + prHex := pr.ToHexStr() + cmd = append(cmd, prHex) + } + + // add sig to cmd + sigHex := sig.ToHexStr() + cmd = append(cmd, sigHex) + + // specify used key + cmd = append(cmd, "--from=val") + + // gas + cmd = append(cmd, "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3") + + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully committed public randomness list") +} + +func (n *NodeConfig) AddFinalitySig(fpBTCPK *bbn.BIP340PubKey, blockHeight uint64, blockLch []byte, finalitySig *bbn.SchnorrEOTSSig) { + n.LogActionF("add finality signature") + + fpBTCPKHex := fpBTCPK.MarshalHex() + blockHeightStr := strconv.FormatUint(blockHeight, 10) + blockLchHex := hex.EncodeToString(blockLch) + finalitySigHex := finalitySig.ToHexStr() + + cmd := []string{"babylond", "tx", "finality", "add-finality-sig", fpBTCPKHex, blockHeightStr, blockLchHex, finalitySigHex, "--from=val", "--gas=500000"} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully added finality signature") +} + +func (n *NodeConfig) AddCovenantUnbondingSigs( + covPK *bbn.BIP340PubKey, + stakingTxHash string, + unbondingTxSig *bbn.BIP340Signature, + slashUnbondingTxSigs []*asig.AdaptorSignature) { + n.LogActionF("adding finality provider signature") + + covPKHex := covPK.MarshalHex() + unbondingTxSigHex := unbondingTxSig.ToHexStr() + + cmd := []string{"babylond", "tx", "btcstaking", "add-covenant-unbonding-sigs", covPKHex, stakingTxHash, unbondingTxSigHex} + for _, sig := range slashUnbondingTxSigs { + cmd = append(cmd, sig.MarshalHex()) + } + cmd = append(cmd, "--from=val") + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully added covenant unbonding sigs") +} + +func (n *NodeConfig) BTCUndelegate(stakingTxHash *chainhash.Hash, delUnbondingSig *schnorr.Signature) { + n.LogActionF("undelegate by using signature on unbonding tx from delegator") + + sigHex := bbn.NewBIP340SignatureFromBTCSig(delUnbondingSig).ToHexStr() + cmd := []string{"babylond", "tx", "btcstaking", "btc-undelegate", stakingTxHash.String(), sigHex, "--from=val"} + + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully added signature on unbonding tx from delegator") +} diff --git a/test/e2e/configurer/chain/node.go b/test/e2e/configurer/chain/node.go index bb51f3a8c..476db4573 100644 --- a/test/e2e/configurer/chain/node.go +++ b/test/e2e/configurer/chain/node.go @@ -2,6 +2,7 @@ package chain import ( "context" + "encoding/hex" "fmt" "regexp" "strings" @@ -30,7 +31,7 @@ type NodeConfig struct { setupTime time.Time } -// NewNodeConfig returens new initialized NodeConfig. +// NewNodeConfig returns new initialized NodeConfig. func NewNodeConfig(t *testing.T, initNode *initialization.Node, initConfig *initialization.NodeConfig, chainId string, containerManager *containers.Manager) *NodeConfig { return &NodeConfig{ Node: *initNode, @@ -71,8 +72,8 @@ func (n *NodeConfig) Run() error { n.t.Logf("started node container: %s", n.Name) return true }, - 2*time.Minute, - time.Second, + 3*time.Minute, + 2*time.Second, "bbn node failed to produce blocks", ) @@ -117,7 +118,7 @@ func (n *NodeConfig) LatestBlockNumber() uint64 { return uint64(status.SyncInfo.LatestBlockHeight) } -func (n *NodeConfig) WaitForCondition(doneCondition func() bool, errormsg string) { +func (n *NodeConfig) WaitForCondition(doneCondition func() bool, errorMsg string) { for i := 0; i < waitUntilrepeatMax; i++ { if !doneCondition() { time.Sleep(waitUntilRepeatPauseTime) @@ -125,7 +126,7 @@ func (n *NodeConfig) WaitForCondition(doneCondition func() bool, errormsg string } return } - n.t.Errorf("node %s timed out waiting for condition. Msg: %s", n.Name, errormsg) + n.t.Errorf("node %s timed out waiting for condition. Msg: %s", n.Name, errorMsg) } func (n *NodeConfig) WaitUntilBtcHeight(height uint64) { @@ -153,7 +154,7 @@ func (n *NodeConfig) extractOperatorAddressIfValidator() error { return nil } - cmd := []string{"babylond", "debug", "addr", n.PublicKey} + cmd := []string{"babylond", "debug", "addr", hex.EncodeToString(n.PublicKey)} n.t.Logf("extracting validator operator addresses for validator: %s", n.Name) _, errBuf, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") if err != nil { diff --git a/test/e2e/configurer/chain/queries.go b/test/e2e/configurer/chain/queries.go index 176c9da5b..48b5dba6a 100644 --- a/test/e2e/configurer/chain/queries.go +++ b/test/e2e/configurer/chain/queries.go @@ -12,14 +12,15 @@ import ( "time" sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/types/query" - - tmabcitypes "github.com/cometbft/cometbft/abci/types" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + cmtabcitypes "github.com/cometbft/cometbft/abci/types" + cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 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" @@ -67,6 +68,25 @@ func (n *NodeConfig) QueryGRPCGateway(path string, queryParams url.Values) ([]by return bz, nil } +// QueryModuleAddress returns the address of a given module +func (n *NodeConfig) QueryModuleAddress(name string) (sdk.AccAddress, error) { + path := fmt.Sprintf("/cosmos/auth/v1beta1/module_accounts/%s", name) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp authtypes.QueryModuleAccountByNameResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return sdk.AccAddress{}, err + } + // cast to account + var account sdk.AccountI + if err := util.EncodingConfig.InterfaceRegistry.UnpackAny(resp.Account, &account); err != nil { + return sdk.AccAddress{}, err + } + + return account.GetAddress(), nil +} + // QueryBalances returns balances at the address. func (n *NodeConfig) QueryBalances(address string) (sdk.Coins, error) { path := fmt.Sprintf("cosmos/bank/v1beta1/balances/%s", address) @@ -87,11 +107,20 @@ func (n *NodeConfig) QuerySupplyOf(denom string) (sdkmath.Int, error) { var supplyResp banktypes.QuerySupplyOfResponse if err := util.Cdc.UnmarshalJSON(bz, &supplyResp); err != nil { - return sdk.NewInt(0), err + return sdkmath.NewInt(0), err } return supplyResp.Amount.Amount, nil } +// QueryBlock gets block at a specific height +func (n *NodeConfig) QueryBlock(height int64) (*cmttypes.Block, error) { + block, err := n.rpcClient.Block(context.Background(), &height) + if err != nil { + return nil, err + } + return block.Block, nil +} + // QueryHashFromBlock gets block hash at a specific height. Otherwise, error. func (n *NodeConfig) QueryHashFromBlock(height int64) (string, error) { block, err := n.rpcClient.Block(context.Background(), &height) @@ -118,13 +147,13 @@ func (n *NodeConfig) QueryLatestBlockTime() time.Time { } // QueryListSnapshots gets all snapshots currently created for a node. -func (n *NodeConfig) QueryListSnapshots() ([]*tmabcitypes.Snapshot, error) { +func (n *NodeConfig) QueryListSnapshots() ([]*cmtabcitypes.Snapshot, error) { abciResponse, err := n.rpcClient.ABCIQuery(context.Background(), "/app/snapshots", nil) if err != nil { return nil, err } - var listSnapshots tmabcitypes.ResponseListSnapshots + var listSnapshots cmtabcitypes.ResponseListSnapshots if err := json.Unmarshal(abciResponse.Response.Value, &listSnapshots); err != nil { return nil, err } @@ -201,6 +230,38 @@ func (n *NodeConfig) QueryTip() (*blc.BTCHeaderInfo, error) { return blcResponse.Header, nil } +func (n *NodeConfig) QueryHeaderDepth(hash string) (uint64, error) { + path := fmt.Sprintf("babylon/btclightclient/v1/depth/%s", hash) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var blcResponse blc.QueryHeaderDepthResponse + if err := util.Cdc.UnmarshalJSON(bz, &blcResponse); err != nil { + return 0, err + } + + return blcResponse.Depth, nil +} + +func (n *NodeConfig) QueryListHeaders(chainID string, pagination *query.PageRequest) (*zctypes.QueryListHeadersResponse, error) { + queryParams := url.Values{} + if pagination != nil { + queryParams.Set("pagination.key", base64.URLEncoding.EncodeToString(pagination.Key)) + queryParams.Set("pagination.limit", strconv.Itoa(int(pagination.Limit))) + } + + path := fmt.Sprintf("babylon/zoneconcierge/v1/headers/%s", chainID) + bz, err := n.QueryGRPCGateway(path, queryParams) + require.NoError(n.t, err) + + var resp zctypes.QueryListHeadersResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + func (n *NodeConfig) QueryFinalizedChainsInfo(chainIDs []string) ([]*zctypes.FinalizedChainInfo, error) { queryParams := url.Values{} for _, chainId := range chainIDs { diff --git a/test/e2e/configurer/chain/queries_btcstaking.go b/test/e2e/configurer/chain/queries_btcstaking.go new file mode 100644 index 000000000..cb55b0ab3 --- /dev/null +++ b/test/e2e/configurer/chain/queries_btcstaking.go @@ -0,0 +1,145 @@ +package chain + +import ( + "fmt" + "net/url" + + "github.com/babylonchain/babylon/test/e2e/util" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + ftypes "github.com/babylonchain/babylon/x/finality/types" + "github.com/stretchr/testify/require" +) + +func (n *NodeConfig) QueryBTCStakingParams() *bstypes.Params { + bz, err := n.QueryGRPCGateway("/babylon/btcstaking/v1/params", url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryParamsResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return &resp.Params +} + +func (n *NodeConfig) QueryFinalityProviders() []*bstypes.FinalityProvider { + bz, err := n.QueryGRPCGateway("/babylon/btcstaking/v1/finality_providers", url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryFinalityProvidersResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.FinalityProviders +} + +func (n *NodeConfig) QueryActiveFinalityProvidersAtHeight(height uint64) []*bstypes.FinalityProviderWithMeta { + path := fmt.Sprintf("/babylon/btcstaking/v1/finality_providers/%d", height) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryActiveFinalityProvidersAtHeightResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.FinalityProviders +} + +func (n *NodeConfig) QueryFinalityProviderDelegations(fpBTCPK string) []*bstypes.BTCDelegatorDelegations { + path := fmt.Sprintf("/babylon/btcstaking/v1/finality_providers/%s/delegations", fpBTCPK) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryFinalityProviderDelegationsResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.BtcDelegatorDelegations +} + +func (n *NodeConfig) QueryBtcDelegation(stakingTxHash string) *bstypes.QueryBTCDelegationResponse { + path := fmt.Sprintf("/babylon/btcstaking/v1/btc_delegations/%s", stakingTxHash) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryBTCDelegationResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return &resp +} + +func (n *NodeConfig) QueryUnbondedDelegations() []*bstypes.BTCDelegation { + queryParams := url.Values{} + queryParams.Add("status", fmt.Sprintf("%d", bstypes.BTCDelegationStatus_UNBONDED)) + bz, err := n.QueryGRPCGateway("/babylon/btcstaking/v1/btc_delegations", queryParams) + require.NoError(n.t, err) + + var resp bstypes.QueryBTCDelegationsResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.BtcDelegations +} + +func (n *NodeConfig) QueryActivatedHeight() uint64 { + bz, err := n.QueryGRPCGateway("/babylon/btcstaking/v1/activated_height", url.Values{}) + require.NoError(n.t, err) + + var resp bstypes.QueryActivatedHeightResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.Height +} + +// TODO: pagination support +func (n *NodeConfig) QueryListPublicRandomness(fpBTCPK *bbn.BIP340PubKey) map[uint64]*bbn.SchnorrPubRand { + path := fmt.Sprintf("/babylon/finality/v1/finality_providers/%s/public_randomness_list", fpBTCPK.MarshalHex()) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp ftypes.QueryListPublicRandomnessResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.PubRandMap +} + +func (n *NodeConfig) QueryVotesAtHeight(height uint64) []bbn.BIP340PubKey { + path := fmt.Sprintf("/babylon/finality/v1/votes/%d", height) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp ftypes.QueryVotesAtHeightResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.BtcPks +} + +// TODO: pagination support +func (n *NodeConfig) QueryListBlocks(status ftypes.QueriedBlockStatus) []*ftypes.IndexedBlock { + values := url.Values{} + values.Set("status", fmt.Sprintf("%d", status)) + bz, err := n.QueryGRPCGateway("/babylon/finality/v1/blocks", values) + require.NoError(n.t, err) + + var resp ftypes.QueryListBlocksResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.Blocks +} + +func (n *NodeConfig) QueryIndexedBlock(height uint64) *ftypes.IndexedBlock { + path := fmt.Sprintf("/babylon/finality/v1/blocks/%d", height) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp ftypes.QueryBlockResponse + err = util.Cdc.UnmarshalJSON(bz, &resp) + require.NoError(n.t, err) + + return resp.Block +} diff --git a/test/e2e/configurer/chain/queries_ibc.go b/test/e2e/configurer/chain/queries_ibc.go new file mode 100644 index 000000000..621f5867c --- /dev/null +++ b/test/e2e/configurer/chain/queries_ibc.go @@ -0,0 +1,69 @@ +package chain + +import ( + "fmt" + "net/url" + + "github.com/babylonchain/babylon/test/e2e/util" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" +) + +func (n *NodeConfig) QueryIBCChannels() (*channeltypes.QueryChannelsResponse, error) { + path := "/ibc/core/channel/v1/channels" + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp channeltypes.QueryChannelsResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (n *NodeConfig) QueryNextSequenceReceive(channelID, portID string) (*channeltypes.QueryNextSequenceReceiveResponse, error) { + path := fmt.Sprintf("/ibc/core/channel/v1/channels/%s/ports/%s/next_sequence", channelID, portID) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp channeltypes.QueryNextSequenceReceiveResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (n *NodeConfig) QueryNextSequenceSend(channelID, portID string) (*channeltypes.QueryNextSequenceSendResponse, error) { + path := fmt.Sprintf("/ibc/core/channel/v1/channels/%s/ports/%s/next_sequence_send", channelID, portID) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp channeltypes.QueryNextSequenceSendResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func (n *NodeConfig) QueryPacketAcknowledgement(channelID string, portID string, sequence uint64) (*channeltypes.QueryPacketAcknowledgementResponse, error) { + path := fmt.Sprintf("/ibc/core/channel/v1/channels/%s/ports/%s/packet_acks/%d", channelID, portID, sequence) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp channeltypes.QueryPacketAcknowledgementResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp, nil +} diff --git a/test/e2e/configurer/chain/queries_incentive.go b/test/e2e/configurer/chain/queries_incentive.go new file mode 100644 index 000000000..5f8dce124 --- /dev/null +++ b/test/e2e/configurer/chain/queries_incentive.go @@ -0,0 +1,68 @@ +package chain + +import ( + "fmt" + "net/url" + + "github.com/babylonchain/babylon/test/e2e/util" + incentivetypes "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func (n *NodeConfig) QueryBTCStakingGauge(height uint64) (*incentivetypes.Gauge, error) { + path := fmt.Sprintf("/babylonchain/babylon/incentive/btc_staking_gauge/%d", height) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp incentivetypes.QueryBTCStakingGaugeResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return resp.Gauge, nil +} + +func (n *NodeConfig) QueryIncentiveParams() (*incentivetypes.Params, error) { + path := "/babylonchain/babylon/incentive/params" + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var resp incentivetypes.QueryParamsResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return &resp.Params, nil +} + +func (n *NodeConfig) QueryRewardGauge(sAddr sdk.AccAddress) (map[string]*incentivetypes.RewardGauge, error) { + path := fmt.Sprintf("/babylonchain/babylon/incentive/address/%s/reward_gauge", sAddr.String()) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + var resp incentivetypes.QueryRewardGaugesResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return resp.RewardGauges, nil +} + +func (n *NodeConfig) QueryBTCTimestampingGauge(epoch uint64) (*incentivetypes.Gauge, error) { + path := fmt.Sprintf("/babylonchain/babylon/incentive/btc_timestamping_gauge/%d", epoch) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return nil, err + } + + var resp incentivetypes.QueryBTCTimestampingGaugeResponse + if err := util.Cdc.UnmarshalJSON(bz, &resp); err != nil { + return nil, err + } + + return resp.Gauge, nil +} diff --git a/test/e2e/configurer/config/constants.go b/test/e2e/configurer/config/constants.go index f399c94c3..09c584507 100644 --- a/test/e2e/configurer/config/constants.go +++ b/test/e2e/configurer/config/constants.go @@ -1,16 +1,10 @@ package config const ( - // if not skipping upgrade, how many blocks we allow for fork to run pre upgrade state creation - ForkHeightPreUpgradeOffset int64 = 60 - // estimated number of blocks it takes to submit for a proposal - PropSubmitBlocks float32 = 10 - // estimated number of blocks it takes to deposit for a proposal + // PropDepositBlocks estimated number of blocks it takes to deposit for a proposal PropDepositBlocks float32 = 10 - // number of blocks it takes to vote for a single validator to vote for a proposal + // PropVoteBlocks number of blocks it takes to vote for a single validator to vote for a proposal PropVoteBlocks float32 = 1.2 - // number of blocks used as a calculation buffer + // PropBufferBlocks number of blocks used as a calculation buffer PropBufferBlocks float32 = 6 - // max retries for json unmarshalling - MaxRetries = 60 ) diff --git a/test/e2e/configurer/factory.go b/test/e2e/configurer/factory.go index 8a587d1b4..bcac549e5 100644 --- a/test/e2e/configurer/factory.go +++ b/test/e2e/configurer/factory.go @@ -6,6 +6,8 @@ import ( "github.com/babylonchain/babylon/test/e2e/configurer/chain" "github.com/babylonchain/babylon/test/e2e/containers" "github.com/babylonchain/babylon/test/e2e/initialization" + zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" ) type Configurer interface { @@ -19,12 +21,19 @@ type Configurer interface { RunValidators() error - RunIBC() error + InstantiateBabylonContract() error + + RunHermesRelayerIBC() error + + // RunCosmosRelayerIBC configures IBC with Go relayer + RunCosmosRelayerIBC() error + + RunIBCTransferChannel() error } var ( // Last nodes are non validator nodes to serve as the ones using relayer. Out - // validators are constantly sending bls transactions which make relayer operatrions + // validators are constantly sending bls transactions which make relayer operations // fail constantly // each started validator container corresponds to one of @@ -89,23 +98,100 @@ var ( IsValidator: false, }, } + ibcConfigChainA = &ibctesting.ChannelConfig{ + PortID: zctypes.PortID, + Order: zctypes.Ordering, + Version: zctypes.Version, + } + ibcConfigChainB = &ibctesting.ChannelConfig{ + PortID: zctypes.PortID, // Will be replaced by the contract address in Phase 2 tests + Order: zctypes.Ordering, + Version: zctypes.Version, + } ) -// New returns a new Configurer. +// NewBTCTimestampingConfigurer returns a new Configurer for BTC timestamping service. // TODO currently only one configuration is available. Consider testing upgrades // when necessary -func New(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { - containerManager, err := containers.NewManager(isDebugLogEnabled) +func NewBTCTimestampingConfigurer(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled, false) if err != nil { return nil, err } return NewCurrentBranchConfigurer(t, []*chain.Config{ - chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA), - chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB), + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA, ibcConfigChainA), + chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB, ibcConfigChainB), }, withIBC(baseSetup), // base set up with IBC containerManager, ), nil } + +func NewIBCTransferConfigurer(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled, false) + if err != nil { + return nil, err + } + + return NewCurrentBranchConfigurer(t, + []*chain.Config{ + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA, ibcConfigChainA), + chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB, ibcConfigChainB), + }, + withIBCTransferChannel(baseSetup), // base set up with IBC + containerManager, + ), nil +} + +// NewBTCTimestampingPhase2Configurer returns a new Configurer for BTC timestamping service (phase 2). +func NewBTCTimestampingPhase2Configurer(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled, false) + if err != nil { + return nil, err + } + + return NewCurrentBranchConfigurer(t, + []*chain.Config{ + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA, ibcConfigChainA), + chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB, ibcConfigChainB), + }, + withPhase2IBC(baseSetup), // IBC setup (requires contract address) + containerManager, + ), nil +} + +// NewBTCTimestampingPhase2RlyConfigurer returns a new Configurer for BTC timestamping service (phase 2), using the Go relayer (rly). +func NewBTCTimestampingPhase2RlyConfigurer(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled, true) + if err != nil { + return nil, err + } + + return NewCurrentBranchConfigurer(t, + []*chain.Config{ + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA, ibcConfigChainA), + chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB, ibcConfigChainB), + }, + withPhase2RlyIBC(baseSetup), // IBC setup with wasmd and Go relayer + containerManager, + ), nil +} + +// NewBTCStakingConfigurer returns a new Configurer for BTC staking service +func NewBTCStakingConfigurer(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled, false) + if err != nil { + return nil, err + } + + return NewCurrentBranchConfigurer(t, + []*chain.Config{ + // we only need 1 chain for testing BTC staking + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA, nil), + }, + baseSetup, // base set up + containerManager, + ), nil +} diff --git a/test/e2e/configurer/setup.go b/test/e2e/configurer/setup.go index 50a6d2c15..21308c458 100644 --- a/test/e2e/configurer/setup.go +++ b/test/e2e/configurer/setup.go @@ -12,12 +12,65 @@ func baseSetup(configurer Configurer) error { } func withIBC(setupHandler setupFn) setupFn { + return func(configurer Configurer) error { + if err := setupHandler(configurer); err != nil { + return err + } + time.Sleep(1 * time.Second) + if err := configurer.RunHermesRelayerIBC(); err != nil { + return err + } + + return nil + } +} + +func withPhase2IBC(setupHandler setupFn) setupFn { + return func(configurer Configurer) error { + if err := setupHandler(configurer); err != nil { + return err + } + time.Sleep(5 * time.Second) + // Instantiate contract on (CZ-like) chain B + if err := configurer.InstantiateBabylonContract(); err != nil { + return err + } + + if err := configurer.RunHermesRelayerIBC(); err != nil { + return err + } + + return nil + } +} + +func withPhase2RlyIBC(setupHandler setupFn) setupFn { return func(configurer Configurer) error { if err := setupHandler(configurer); err != nil { return err } time.Sleep(5 * time.Second) - if err := configurer.RunIBC(); err != nil { + // Instantiate contract on (CZ-like) chain B + if err := configurer.InstantiateBabylonContract(); err != nil { + return err + } + + if err := configurer.RunCosmosRelayerIBC(); err != nil { + return err + } + + return nil + } +} + +func withIBCTransferChannel(setupHandler setupFn) setupFn { + return func(configurer Configurer) error { + if err := setupHandler(configurer); err != nil { + return err + } + time.Sleep(5 * time.Second) + + if err := configurer.RunIBCTransferChannel(); err != nil { return err } diff --git a/test/e2e/containers/config.go b/test/e2e/containers/config.go index 90b8416cc..f253858fa 100644 --- a/test/e2e/containers/config.go +++ b/test/e2e/containers/config.go @@ -12,18 +12,30 @@ const ( // name of babylon container produced by running `make localnet-build-env` BabylonContainerName = "babylonchain/babylond" - relayerRepository = "informalsystems/hermes" - relayerTag = "1.4.0" + hermesRelayerRepository = "informalsystems/hermes" + // TODO: Replace with version tag once we have a working version + hermesRelayerTag = "master" + // Built using the `build-cosmos-relayer-docker` target on an Intel (amd64) machine and pushed to ECR + cosmosRelayerRepository = "public.ecr.aws/t9e9i3h0/cosmos-relayer" + // TODO: Replace with version tag once we have a working version + cosmosRelayerTag = "main" ) -// Returns ImageConfig needed for running e2e test. +// NewImageConfig returns ImageConfig needed for running e2e test. // If isUpgrade is true, returns images for running the upgrade // If isFork is true, utilizes provided fork height to initiate fork logic -func NewImageConfig() ImageConfig { - config := ImageConfig{ - RelayerRepository: relayerRepository, - RelayerTag: relayerTag, +func NewImageConfig(isCosmosRelayer bool) ImageConfig { + if isCosmosRelayer { + config := ImageConfig{ + RelayerRepository: cosmosRelayerRepository, + RelayerTag: cosmosRelayerTag, + } + return config + } else { + config := ImageConfig{ + RelayerRepository: hermesRelayerRepository, + RelayerTag: hermesRelayerTag, + } + return config } - - return config } diff --git a/test/e2e/containers/containers.go b/test/e2e/containers/containers.go index e681753b5..8ce88dbd5 100644 --- a/test/e2e/containers/containers.go +++ b/test/e2e/containers/containers.go @@ -16,7 +16,8 @@ import ( ) const ( - hermesContainerName = "hermes-relayer" + hermesContainerName = "hermes-relayer" + cosmosRelayerContainerName = "rly-relayer" // The maximum number of times debug logs are printed to console // per CLI command. maxDebugLogsPerCommand = 3 @@ -35,10 +36,10 @@ type Manager struct { } // NewManager creates a new Manager instance and initializes -// all Docker specific utilies. Returns an error if initialiation fails. -func NewManager(isDebugLogEnabled bool) (docker *Manager, err error) { +// all Docker specific utilities. Returns an error if initialization fails. +func NewManager(isDebugLogEnabled bool, isCosmosRelayer bool) (docker *Manager, err error) { docker = &Manager{ - ImageConfig: NewImageConfig(), + ImageConfig: NewImageConfig(isCosmosRelayer), resources: make(map[string]*dockertest.Resource), isDebugLogEnabled: isDebugLogEnabled, } @@ -123,7 +124,7 @@ func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string, errBufString := errBuf.String() // Note that this does not match all errors. - // This only works if CLI outpurs "Error" or "error" + // This only works if CLI outputs "Error" or "error" // to stderr. if (errRegex.MatchString(errBufString) || m.isDebugLogEnabled) && maxDebugLogTriesLeft > 0 { t.Log("\nstderr:") @@ -175,7 +176,6 @@ func (m *Manager) RunHermesResource(chainAID, osmoARelayerNodeName, osmoAValMnem PortBindings: map[docker.Port][]docker.PortBinding{ "3031/tcp": {{HostIP: "", HostPort: "3031"}}, }, - Platform: "linux/x86_64", Env: []string{ fmt.Sprintf("BBN_A_E2E_CHAIN_ID=%s", chainAID), fmt.Sprintf("BBN_B_E2E_CHAIN_ID=%s", chainBID), @@ -199,6 +199,47 @@ func (m *Manager) RunHermesResource(chainAID, osmoARelayerNodeName, osmoAValMnem return hermesResource, nil } +// RunRlyResource runs a Cosmos relayer container. Returns the container resource and error if any. +// the name of the cosmos container is "--relayer" +func (m *Manager) RunRlyResource(chainAID, osmoARelayerNodeName, osmoAValMnemonic, chainAIbcPort, chainBID, osmoBRelayerNodeName, osmoBValMnemonic, chainBIbcPort string, rlyCfgPath string) (*dockertest.Resource, error) { + rlyResource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: cosmosRelayerContainerName, + Repository: m.RelayerRepository, + Tag: m.RelayerTag, + NetworkID: m.network.Network.ID, + Cmd: []string{ + "start", + }, + User: "root:root", + Mounts: []string{ + fmt.Sprintf("%s/:/root/rly", rlyCfgPath), + }, + Env: []string{ + fmt.Sprintf("BBN_A_E2E_CHAIN_ID=%s", chainAID), + fmt.Sprintf("BBN_B_E2E_CHAIN_ID=%s", chainBID), + fmt.Sprintf("BBN_A_E2E_VAL_MNEMONIC=%s", osmoAValMnemonic), + fmt.Sprintf("BBN_B_E2E_VAL_MNEMONIC=%s", osmoBValMnemonic), + fmt.Sprintf("BBN_A_E2E_VAL_HOST=%s", osmoARelayerNodeName), + fmt.Sprintf("BBN_B_E2E_VAL_HOST=%s", osmoBRelayerNodeName), + fmt.Sprintf("CHAIN_A_IBC_PORT=%s", chainAIbcPort), + fmt.Sprintf("CHAIN_B_IBC_PORT=%s", chainBIbcPort), + }, + Entrypoint: []string{ + "sh", + "-c", + "chmod +x /root/rly/rly_bootstrap.sh && /root/rly/rly_bootstrap.sh", + }, + }, + noRestart, + ) + if err != nil { + return nil, err + } + m.resources[cosmosRelayerContainerName] = rlyResource + return rlyResource, nil +} + // 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) { @@ -218,7 +259,6 @@ func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir st "babylond start --home /home/babylon/babylondata", }, ExposedPorts: []string{"26656", "26657", "1317", "9090"}, - 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_setup_test.go b/test/e2e/e2e_setup_test.go deleted file mode 100644 index 3a0113f69..000000000 --- a/test/e2e/e2e_setup_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package e2e - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - configurer "github.com/babylonchain/babylon/test/e2e/configurer" -) - -type IntegrationTestSuite struct { - suite.Suite - - configurer configurer.Configurer -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up e2e integration test suite...") - var ( - err error - ) - - // The e2e test flow is as follows: - // - // 1. Configure two chains - chan A and chain B. - // * For each chain, set up several validator nodes - // * Initialize configs and genesis for all them. - // 2. Start both networks. - // 3. Run IBC relayer betweeen the two chains. - // 4. Execute various e2e tests, including IBC - s.configurer, err = configurer.New(s.T(), true) - - s.Require().NoError(err) - - err = s.configurer.ConfigureChains() - s.Require().NoError(err) - - err = s.configurer.RunSetup() - s.Require().NoError(err) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - err := s.configurer.ClearResources() - s.Require().NoError(err) -} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 1b9411895..422123171 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -4,118 +4,35 @@ package e2e import ( - "crypto/sha256" - "encoding/hex" - "fmt" - "strconv" + "testing" - "github.com/babylonchain/babylon/test/e2e/initialization" - ct "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" ) -// Most simple test, just checking that two chains are up and connected through -// ibc -func (s *IntegrationTestSuite) TestConnectIbc() { - chainA := s.configurer.GetChainConfig(0) - chainB := s.configurer.GetChainConfig(1) - _, err := chainA.GetDefaultNode() - s.NoError(err) - _, err = chainB.GetDefaultNode() - s.NoError(err) +// IBCTransferTestSuite tests IBC transfer end-to-end +func TestIBCTranferTestSuite(t *testing.T) { + suite.Run(t, new(IBCTransferTestSuite)) } -func (s *IntegrationTestSuite) TestIbcCheckpointing() { - chainA := s.configurer.GetChainConfig(0) - chainA.WaitUntilHeight(35) - - nonValidatorNode, err := chainA.GetNodeAtIndex(2) - s.NoError(err) - - // Query checkpoint chain info for opposing chain - chainsInfo, err := nonValidatorNode.QueryChainsInfo([]string{initialization.ChainBID}) - s.NoError(err) - s.Equal(chainsInfo[0].ChainId, initialization.ChainBID) - - // Finalize epoch 1,2,3 , as first headers of opposing chain are in epoch 3 - var ( - startEpochNum uint64 = 1 - endEpochNum uint64 = 3 - ) - - nonValidatorNode.FinalizeSealedEpochs(startEpochNum, endEpochNum) - - endEpoch, err := nonValidatorNode.QueryRawCheckpoint(endEpochNum) - s.NoError(err) - s.Equal(endEpoch.Status, ct.Finalized) - - // Check we have epoch info for opposing chain and some basic assertions - epochChainsInfo, err := nonValidatorNode.QueryEpochChainsInfo(endEpochNum, []string{initialization.ChainBID}) - s.NoError(err) - s.Equal(epochChainsInfo[0].ChainId, initialization.ChainBID) - s.Equal(epochChainsInfo[0].LatestHeader.BabylonEpoch, endEpochNum) - - // Check we have finalized epoch info for opposing chain and some basic assertions - finalizedChainsInfo, err := nonValidatorNode.QueryFinalizedChainsInfo([]string{initialization.ChainBID}) - s.NoError(err) - - // TODO Add more assertion here. Maybe check proofs ? - s.Equal(finalizedChainsInfo[0].FinalizedChainInfo.ChainId, initialization.ChainBID) - s.Equal(finalizedChainsInfo[0].EpochInfo.EpochNumber, endEpochNum) - - currEpoch, err := nonValidatorNode.QueryCurrentEpoch() - s.NoError(err) - - heightAtEndedEpoch, err := nonValidatorNode.QueryLightClientHeightEpochEnd(currEpoch - 1) - s.NoError(err) - - if heightAtEndedEpoch == 0 { - // we can only assert, that btc lc height is larger than 0. - s.FailNow(fmt.Sprintf("Light client height should be > 0 on epoch %d", currEpoch-1)) - } - - chainB := s.configurer.GetChainConfig(1) - _, err = chainB.GetDefaultNode() - s.NoError(err) +// TestBTCTimestampingTestSuite tests BTC timestamping protocol end-to-end +func TestBTCTimestampingTestSuite(t *testing.T) { + suite.Run(t, new(BTCTimestampingTestSuite)) } -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)) +// TestBTCTimestampingPhase2HermesTestSuite tests BTC timestamping phase 2 protocol end-to-end, +// with the Hermes relayer +// TODO: Uncomment once we have a working Hermes version / release +//func TestBTCTimestampingPhase2HermesTestSuite(t *testing.T) { +// suite.Run(t, new(BTCTimestampingPhase2HermesTestSuite)) +//} + +// TestBTCTimestampingPhase2RlyTestSuite tests BTC timestamping phase 2 protocol end-to-end, +// with the Go relayer +func TestBTCTimestampingPhase2RlyTestSuite(t *testing.T) { + suite.Run(t, new(BTCTimestampingPhase2RlyTestSuite)) +} - 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) +// TestBTCStakingTestSuite tests BTC staking protocol end-to-end +func TestBTCStakingTestSuite(t *testing.T) { + suite.Run(t, new(BTCStakingTestSuite)) } diff --git a/test/e2e/env.sh b/test/e2e/env.sh new file mode 100755 index 000000000..0c5f42298 --- /dev/null +++ b/test/e2e/env.sh @@ -0,0 +1,7 @@ +: + +export homeDir="../../.testnets/node0/babylond" +export chainId="chain-test" +export key="test-spending-key" +export keyringBackend="--keyring-backend=test" +export nodeUrl="tcp://localhost:26657" diff --git a/test/e2e/ibc_transfer_e2e_test.go b/test/e2e/ibc_transfer_e2e_test.go new file mode 100644 index 000000000..8f1848c54 --- /dev/null +++ b/test/e2e/ibc_transfer_e2e_test.go @@ -0,0 +1,52 @@ +package e2e + +import ( + "time" + + "github.com/babylonchain/babylon/test/e2e/configurer" + "github.com/babylonchain/babylon/test/e2e/initialization" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" +) + +type IBCTransferTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func (s *IBCTransferTestSuite) SetupSuite() { + s.T().Log("setting up IBC test suite...") + var ( + err error + ) + + s.configurer, err = configurer.NewIBCTransferConfigurer(s.T(), true) + + s.Require().NoError(err) + + err = s.configurer.ConfigureChains() + s.Require().NoError(err) + + err = s.configurer.RunSetup() + s.Require().NoError(err) +} + +func (s *IBCTransferTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} + +func (s *IBCTransferTestSuite) Test1IBCTransfer() { + babylonChain := s.configurer.GetChainConfig(0) + + babylonNode, err := babylonChain.GetNodeAtIndex(2) + s.NoError(err) + + sender := initialization.ValidatorWalletName + babylonNode.SendIBCTransfer(sender, sender, "", sdk.NewInt64Coin("ubbn", 1000)) + + time.Sleep(1 * time.Minute) + + // TODO: check the transfer is successful. Right now this is done by manually looking at the log +} diff --git a/test/e2e/initialization/config.go b/test/e2e/initialization/config.go index 3903f49f3..96f39e72e 100644 --- a/test/e2e/initialization/config.go +++ b/test/e2e/initialization/config.go @@ -6,14 +6,10 @@ import ( "path/filepath" "time" - "github.com/babylonchain/babylon/privval" - bbn "github.com/babylonchain/babylon/types" - btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - blctypes "github.com/babylonchain/babylon/x/btclightclient/types" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - tmjson "github.com/cometbft/cometbft/libs/json" + "cosmossdk.io/math" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - ed25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -21,9 +17,16 @@ import ( crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" staketypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/gogoproto/proto" + "github.com/babylonchain/babylon/privval" + bbn "github.com/babylonchain/babylon/types" + btccheckpointtypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + blctypes "github.com/babylonchain/babylon/x/btclightclient/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + "github.com/babylonchain/babylon/test/e2e/util" ) @@ -64,9 +67,9 @@ const ( ) var ( - StakeAmountIntA = sdk.NewInt(StakeAmountA) + StakeAmountIntA = math.NewInt(StakeAmountA) StakeAmountCoinA = sdk.NewCoin(BabylonDenom, StakeAmountIntA) - StakeAmountIntB = sdk.NewInt(StakeAmountB) + StakeAmountIntB = math.NewInt(StakeAmountB) StakeAmountCoinB = sdk.NewCoin(BabylonDenom, StakeAmountIntB) InitBalanceStrA = fmt.Sprintf("%d%s", BabylonBalanceA, BabylonDenom) @@ -213,6 +216,11 @@ func initGenesis(chain *internalChain, votingPeriod, expeditedVotingPeriod time. return err } + err = updateModuleGenesis(appGenState, minttypes.ModuleName, &minttypes.GenesisState{}, updateMintGenesis) + if err != nil { + return err + } + err = updateModuleGenesis(appGenState, staketypes.ModuleName, &staketypes.GenesisState{}, updateStakeGenesis) if err != nil { return err @@ -250,16 +258,16 @@ func initGenesis(chain *internalChain, votingPeriod, expeditedVotingPeriod time. genDoc.AppState = bz - genesisJson, err := tmjson.MarshalIndent(genDoc, "", " ") - if err != nil { - return err - } - // write the updated genesis file to each validator for _, val := range chain.nodes { - if err := util.WriteFile(filepath.Join(val.configDir(), "config", "genesis.json"), genesisJson); err != nil { - return err + path := filepath.Join(val.configDir(), "config", "genesis.json") + + // We need to use genutil.ExportGenesisFile to marshal and write the genesis file + // to use correct json encoding. + if err = genutil.ExportGenesisFile(genDoc, path); err != nil { + return fmt.Errorf("failed to export app genesis state: %w", err) } + } return nil } @@ -280,14 +288,18 @@ func updateBankGenesis(bankGenState *banktypes.GenesisState) { }) } +func updateMintGenesis(mintGenState *minttypes.GenesisState) { + mintGenState.Params.MintDenom = BabylonDenom +} + func updateStakeGenesis(stakeGenState *staketypes.GenesisState) { stakeGenState.Params = staketypes.Params{ BondDenom: BabylonDenom, MaxValidators: 100, MaxEntries: 7, HistoricalEntries: 10000, - UnbondingTime: 240000000000, - MinCommissionRate: sdk.ZeroDec(), + UnbondingTime: staketypes.DefaultUnbondingTime, + MinCommissionRate: math.LegacyZeroDec(), } } @@ -360,7 +372,7 @@ func updateCheckpointingGenesis(c *internalChain) func(*checkpointingtypes.Genes panic("It should be possible to build proof of possesion from validator private keys") } - valPubKey, err := cryptocodec.FromTmPubKeyInterface(node.consensusKey.PubKey) + valPubKey, err := cryptocodec.FromCmtPubKeyInterface(node.consensusKey.PubKey) if err != nil { panic("It should be possible to retrieve validator public key") diff --git a/test/e2e/initialization/export.go b/test/e2e/initialization/export.go index 8e4224164..1da2629ba 100644 --- a/test/e2e/initialization/export.go +++ b/test/e2e/initialization/export.go @@ -1,6 +1,10 @@ package initialization -import "fmt" +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) type ChainMeta struct { DataDir string `json:"dataDir"` @@ -12,7 +16,8 @@ type Node struct { ConfigDir string `json:"configDir"` Mnemonic string `json:"mnemonic"` PublicAddress string `json:"publicAddress"` - PublicKey string `json:"publicKey"` + SecretKey cryptotypes.PrivKey + PublicKey []byte `json:"publicKey"` PeerId string `json:"peerId"` IsValidator bool `json:"isValidator"` } diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index f3ede8b3b..5c0bed9d5 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -8,14 +8,16 @@ import ( "path/filepath" "strings" - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/privval" - bbn "github.com/babylonchain/babylon/types" - tmconfig "github.com/cometbft/cometbft/config" - tmed25519 "github.com/cometbft/cometbft/crypto/ed25519" - tmos "github.com/cometbft/cometbft/libs/os" + "cosmossdk.io/log" + "cosmossdk.io/math" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + cmtconfig "github.com/cometbft/cometbft/config" + cmted25519 "github.com/cometbft/cometbft/crypto/ed25519" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/p2p" - tmtypes "github.com/cometbft/cometbft/types" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/client/flags" sdkcrypto "github.com/cosmos/cosmos-sdk/crypto" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -23,19 +25,23 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/server" srvconfig "github.com/cosmos/cosmos-sdk/server/config" + simsutils "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" sdktx "github.com/cosmos/cosmos-sdk/types/tx" - txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" + sdksigning "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/go-bip39" "github.com/spf13/viper" - "github.com/babylonchain/babylon/test/e2e/util" - babylonApp "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/cmd/babylond/cmd" + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/privval" + "github.com/babylonchain/babylon/test/e2e/util" + bbn "github.com/babylonchain/babylon/types" ) type internalNode struct { @@ -56,18 +62,18 @@ func newNode(chain *internalChain, nodeConfig *NodeConfig) (*internalNode, error moniker: fmt.Sprintf("%s-node-%s", chain.chainMeta.Id, nodeConfig.Name), isValidator: nodeConfig.IsValidator, } - // generate genesis files - if err := node.init(); err != nil { + // creating keys comes before init + if err := node.createKey(ValidatorWalletName); err != nil { return nil, err } - // create keys - if err := node.createKey(ValidatorWalletName); err != nil { + if err := node.createConsensusKey(); err != nil { return nil, err } - if err := node.createNodeKey(); err != nil { + // generate genesis files + if err := node.init(); err != nil { return nil, err } - if err := node.createConsensusKey(); err != nil { + if err := node.createNodeKey(); err != nil { return nil, err } node.createAppConfig(nodeConfig) @@ -81,15 +87,15 @@ func (n *internalNode) configDir() string { func (n *internalNode) buildCreateValidatorMsg(amount sdk.Coin) (sdk.Msg, error) { description := stakingtypes.NewDescription(n.moniker, "", "", "", "") commissionRates := stakingtypes.CommissionRates{ - Rate: sdk.MustNewDecFromStr("0.1"), - MaxRate: sdk.MustNewDecFromStr("0.2"), - MaxChangeRate: sdk.MustNewDecFromStr("0.01"), + Rate: math.LegacyMustNewDecFromStr("0.1"), + MaxRate: math.LegacyMustNewDecFromStr("0.2"), + MaxChangeRate: math.LegacyMustNewDecFromStr("0.01"), } // get the initial validator min self delegation - minSelfDelegation, _ := sdk.NewIntFromString("1") + minSelfDelegation, _ := math.NewIntFromString("1") - valPubKey, err := cryptocodec.FromTmPubKeyInterface(n.consensusKey.PubKey) + valPubKey, err := cryptocodec.FromCmtPubKeyInterface(n.consensusKey.PubKey) if err != nil { return nil, err } @@ -101,7 +107,7 @@ func (n *internalNode) buildCreateValidatorMsg(amount sdk.Coin) (sdk.Msg, error) } return stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), + sdk.ValAddress(addr).String(), valPubKey, amount, description, @@ -129,7 +135,6 @@ func (n *internalNode) createAppConfig(nodeConfig *NodeConfig) { appConfig.MinGasPrices = fmt.Sprintf("%s%s", MinGasPrice, BabylonDenom) appConfig.StateSync.SnapshotInterval = nodeConfig.SnapshotInterval appConfig.StateSync.SnapshotKeepRecent = nodeConfig.SnapshotKeepRecent - appConfig.SignerConfig.KeyName = ValidatorWalletName appConfig.BtcConfig.Network = string(bbn.BtcSimnet) appConfig.GRPC.Enable = true appConfig.GRPC.Address = "0.0.0.0:9090" @@ -164,17 +169,16 @@ func (n *internalNode) createConsensusKey() error { config.Moniker = n.moniker pvKeyFile := config.PrivValidatorKeyFile() - if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { + if err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { return err } pvStateFile := config.PrivValidatorStateFile() - if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { return err } - // privval.LoadOrGenWrappedFilePV() - privKey := tmed25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) + privKey := cmted25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) filePV := privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) @@ -239,7 +243,6 @@ func (n *internalNode) export() *Node { } pub, err := n.keyInfo.GetPubKey() - if err != nil { panic("pub key should be correct") } @@ -249,7 +252,8 @@ func (n *internalNode) export() *Node { ConfigDir: n.configDir(), Mnemonic: n.mnemonic, PublicAddress: addr.String(), - PublicKey: pub.Address().String(), + SecretKey: n.privateKey, + PublicKey: pub.Bytes(), PeerId: n.peerId, IsValidator: n.isValidator, } @@ -259,13 +263,13 @@ func (n *internalNode) getNodeKey() *p2p.NodeKey { return &n.nodeKey } -func (n *internalNode) getGenesisDoc() (*tmtypes.GenesisDoc, error) { +func (n *internalNode) getAppGenesis() (*genutiltypes.AppGenesis, error) { serverCtx := server.NewDefaultContext() config := serverCtx.Config config.SetRoot(n.configDir()) genFile := config.GenesisFile() - doc := &tmtypes.GenesisDoc{} + appGenesis := &genutiltypes.AppGenesis{} if _, err := os.Stat(genFile); err != nil { if !os.IsNotExist(err) { @@ -274,13 +278,13 @@ func (n *internalNode) getGenesisDoc() (*tmtypes.GenesisDoc, error) { } else { var err error - doc, err = tmtypes.GenesisDocFromFile(genFile) + _, appGenesis, err = genutiltypes.GenesisStateFromGenFile(genFile) if err != nil { return nil, fmt.Errorf("failed to read genesis doc from file: %w", err) } } - return doc, nil + return appGenesis, nil } func (n *internalNode) init() error { @@ -294,27 +298,46 @@ func (n *internalNode) init() error { config.SetRoot(n.configDir()) config.Moniker = n.moniker - genDoc, err := n.getGenesisDoc() + appOptions := make(simsutils.AppOptionsMap, 0) + appOptions[flags.FlagHome] = n.configDir() + appOptions["btc-config.network"] = string(bbn.BtcSimnet) + + privSigner := &babylonApp.PrivSigner{WrappedPV: &privval.WrappedFilePV{Key: n.consensusKey}} + // Create a temp app to get the default genesis state + tempApp := babylonApp.NewBabylonApp( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + map[int64]bool{}, + 0, + privSigner, + appOptions, + []wasmkeeper.Option{}) + + appGenesis, err := n.getAppGenesis() if err != nil { return err } - appState, err := json.MarshalIndent(babylonApp.ModuleBasics.DefaultGenesis(util.Cdc), "", " ") + appState, err := json.MarshalIndent(tempApp.DefaultGenesis(), "", " ") if err != nil { return fmt.Errorf("failed to JSON encode app genesis state: %w", err) } - genDoc.ChainID = n.chain.chainMeta.Id - genDoc.Validators = nil - genDoc.AppState = appState - genDoc.ConsensusParams = tmtypes.DefaultConsensusParams() - genDoc.ConsensusParams.Block.MaxGas = babylonApp.DefaultGasLimit + appGenesis.ChainID = n.chain.chainMeta.Id + appGenesis.AppState = appState + appGenesis.Consensus = &genutiltypes.ConsensusGenesis{ + Params: cmttypes.DefaultConsensusParams(), + } + appGenesis.Consensus.Params.Block.MaxGas = babylonApp.DefaultGasLimit + appGenesis.Consensus.Params.ABCI.VoteExtensionsEnableHeight = babylonApp.DefaultVoteExtensionsEnableHeight - if err = genutil.ExportGenesisFile(genDoc, config.GenesisFile()); err != nil { + if err = genutil.ExportGenesisFile(appGenesis, config.GenesisFile()); err != nil { return fmt.Errorf("failed to export app genesis state: %w", err) } - tmconfig.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + cmtconfig.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) return nil } @@ -333,15 +356,15 @@ func (n *internalNode) createMnemonic() (string, error) { } func (n *internalNode) initNodeConfigs(persistentPeers []string) error { - tmCfgPath := filepath.Join(n.configDir(), "config", "config.toml") + cmtCfgPath := filepath.Join(n.configDir(), "config", "config.toml") vpr := viper.New() - vpr.SetConfigFile(tmCfgPath) + vpr.SetConfigFile(cmtCfgPath) if err := vpr.ReadInConfig(); err != nil { return err } - valConfig := tmconfig.DefaultConfig() + valConfig := cmtconfig.DefaultConfig() if err := vpr.Unmarshal(valConfig); err != nil { return err } @@ -351,35 +374,35 @@ func (n *internalNode) initNodeConfigs(persistentPeers []string) error { valConfig.P2P.ExternalAddress = fmt.Sprintf("%s:%d", n.moniker, 26656) valConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" valConfig.StateSync.Enable = false - valConfig.LogLevel = "info" + valConfig.LogLevel = "debug" valConfig.P2P.PersistentPeers = strings.Join(persistentPeers, ",") - valConfig.Storage.DiscardABCIResponses = true + valConfig.Storage.DiscardABCIResponses = false - tmconfig.WriteConfigFile(tmCfgPath, valConfig) + cmtconfig.WriteConfigFile(cmtCfgPath, valConfig) return nil } func (n *internalNode) initStateSyncConfig(trustHeight int64, trustHash string, stateSyncRPCServers []string) error { - tmCfgPath := filepath.Join(n.configDir(), "config", "config.toml") + cmtCfgPath := filepath.Join(n.configDir(), "config", "config.toml") vpr := viper.New() - vpr.SetConfigFile(tmCfgPath) + vpr.SetConfigFile(cmtCfgPath) if err := vpr.ReadInConfig(); err != nil { return err } - valConfig := tmconfig.DefaultConfig() + valConfig := cmtconfig.DefaultConfig() if err := vpr.Unmarshal(valConfig); err != nil { return err } - valConfig.StateSync = tmconfig.DefaultStateSyncConfig() + valConfig.StateSync = cmtconfig.DefaultStateSyncConfig() valConfig.StateSync.Enable = true valConfig.StateSync.TrustHeight = trustHeight valConfig.StateSync.TrustHash = trustHash valConfig.StateSync.RPCServers = stateSyncRPCServers - tmconfig.WriteConfigFile(tmCfgPath, valConfig) + cmtconfig.WriteConfigFile(cmtCfgPath, valConfig) return nil } @@ -396,18 +419,23 @@ func (n *internalNode) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) { txBuilder.SetFeeAmount(sdk.NewCoins()) txBuilder.SetGasLimit(uint64(200000 * len(msgs))) + addr, err := n.keyInfo.GetAddress() + if err != nil { + return nil, err + } + pub, err := n.keyInfo.GetPubKey() + if err != nil { + return nil, err + } // TODO: Find a better way to sign this tx with less code. signerData := authsigning.SignerData{ ChainID: n.chain.chainMeta.Id, AccountNumber: 0, Sequence: 0, + Address: addr.String(), + PubKey: pub, } - pub, err := n.keyInfo.GetPubKey() - - if err != nil { - return nil, err - } // For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on // TxBuilder under the hood, and SignerInfos is needed to generate the sign // bytes. This is the reason for setting SetSignatures here, with a nil @@ -416,10 +444,10 @@ func (n *internalNode) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) { // Note: This line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it // also doesn't affect its generated sign bytes, so for code's simplicity // sake, we put it here. - sig := txsigning.SignatureV2{ + sig := sdksigning.SignatureV2{ PubKey: pub, - Data: &txsigning.SingleSignatureData{ - SignMode: txsigning.SignMode_SIGN_MODE_DIRECT, + Data: &sdksigning.SingleSignatureData{ + SignMode: sdksigning.SignMode_SIGN_MODE_DIRECT, Signature: nil, }, Sequence: 0, @@ -429,8 +457,10 @@ func (n *internalNode) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) { return nil, err } - bytesToSign, err := util.EncodingConfig.TxConfig.SignModeHandler().GetSignBytes( - txsigning.SignMode_SIGN_MODE_DIRECT, + bytesToSign, err := authsigning.GetSignBytesAdapter( + sdk.Context{}, // TODO: this is an empty context + util.EncodingConfig.TxConfig.SignModeHandler(), + sdksigning.SignMode_SIGN_MODE_DIRECT, signerData, txBuilder.GetTx(), ) @@ -447,10 +477,10 @@ func (n *internalNode) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) { return nil, err } - sig = txsigning.SignatureV2{ + sig = sdksigning.SignatureV2{ PubKey: pub, - Data: &txsigning.SingleSignatureData{ - SignMode: txsigning.SignMode_SIGN_MODE_DIRECT, + Data: &sdksigning.SingleSignatureData{ + SignMode: sdksigning.SignMode_SIGN_MODE_DIRECT, Signature: sigBytes, }, Sequence: 0, diff --git a/test/e2e/scripts/copy_local_wasm.sh b/test/e2e/scripts/copy_local_wasm.sh new file mode 100755 index 000000000..66e03b7e5 --- /dev/null +++ b/test/e2e/scripts/copy_local_wasm.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -o errexit -o nounset -o pipefail +command -v shellcheck >/dev/null && shellcheck "$0" + +CONTRACT="babylon_contract" +OUTPUT_FOLDER="$(dirname "$0")/../bytecode" + +echo "DEV-only: copy from local built instead of downloading" + +cp -f ../../../babylon-contract/artifacts/${CONTRACT}.wasm "$OUTPUT_FOLDER/" + +cd ../../../babylon-contract +TAG=$(git rev-parse HEAD) +cd - 2>/dev/null +echo "$TAG" >"$OUTPUT_FOLDER/version.txt" diff --git a/test/e2e/scripts/download_release.sh b/test/e2e/scripts/download_release.sh new file mode 100755 index 000000000..77e35985d --- /dev/null +++ b/test/e2e/scripts/download_release.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -o nounset -o pipefail +command -v shellcheck >/dev/null && shellcheck "$0" + +OWNER="babylonchain" +REPO="babylon-contract" +CONTRACT="babylon_contract" +OUTPUT_FOLDER="$(dirname "$0")/../bytecode" + +[ -z "$GITHUB_API_TOKEN" ] && echo "Error: Please define GITHUB_API_TOKEN variable." >&2 && exit 1 + +[ $# -ne 1 ] && echo "Usage: $0 " && exit 1 +type curl >&2 + +TAG="$1" + +GH_API="https://api.github.com" +GH_REPO="$GH_API/repos/$OWNER/$REPO" +GH_TAGS="$GH_REPO/releases/tags/$TAG" +AUTH="Authorization: token $GITHUB_API_TOKEN" + +# Validate token +curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } + +# Read asset tags +RESPONSE=$(curl -sH "$AUTH" "$GH_TAGS") +# Get id of the contract +ID=$(echo "$RESPONSE" | grep -C3 "name.:.\+$CONTRACT.wasm" | grep -w id | cut -f1 -d, | awk '{print $2}') + +[ -z "$ID" ] && echo "Error: Failed to get asset id, response: $RESPONSE" | awk 'length($0)<100' >&2 && exit 1 +GH_ASSET="$GH_REPO/releases/assets/$ID" + +# Download asset file +echo -n "Downloading asset..." >&2 +curl -s -L -H "Authorization: token $GITHUB_API_TOKEN" -H 'Accept: application/octet-stream' "$GH_ASSET" >"$OUTPUT_FOLDER/$CONTRACT.wasm" +echo "$TAG" >"$OUTPUT_FOLDER/version.txt" +echo "done." >&2 diff --git a/test/e2e/scripts/hermes_bootstrap.sh b/test/e2e/scripts/hermes_bootstrap.sh index cfb3702bd..d9b2ccbac 100644 --- a/test/e2e/scripts/hermes_bootstrap.sh +++ b/test/e2e/scripts/hermes_bootstrap.sh @@ -2,16 +2,19 @@ set -ex -# initialize Hermes relayer configuration -mkdir -p /root/.hermes/ -touch /root/.hermes/config.toml +RELAYER_CONF_DIR=/root/.hermes -echo $BBN_A_E2E_VAL_MNEMONIC > /root/.hermes/BBN_A_MNEMONIC.txt -echo $BBN_B_E2E_VAL_MNEMONIC > /root/.hermes/BBN_B_MNEMONIC.txt -# setup Hermes relayer configuration -tee /root/.hermes/config.toml <$RELAYER_CONF_DIR/BBN_A_MNEMONIC.txt +echo $BBN_B_E2E_VAL_MNEMONIC >$RELAYER_CONF_DIR/BBN_B_MNEMONIC.txt + +# Setup Hermes relayer configuration +cat <$RELAYER_CONF [global] -log_level = 'trace' +log_level = 'debug' [mode] [mode.clients] enabled = true @@ -35,40 +38,42 @@ enabled = true host = '127.0.0.1' port = 3001 [[chains]] +type = "CosmosSdk" id = '$BBN_A_E2E_CHAIN_ID' rpc_addr = 'http://$BBN_A_E2E_VAL_HOST:26657' grpc_addr = 'http://$BBN_A_E2E_VAL_HOST:9090' -websocket_addr = 'ws://$BBN_A_E2E_VAL_HOST:26657/websocket' +event_source = { mode = 'push', url = 'ws://$BBN_A_E2E_VAL_HOST:26657/websocket', batch_delay = '500ms' } rpc_timeout = '10s' account_prefix = 'bbn' key_name = 'val01-bbn-a' store_prefix = 'ibc' -max_gas = 6000000 -gas_price = { price = 0.000, denom = 'ubbn' } -gas_multiplier = 1.1 +max_gas = 50000000 +gas_price = { price = 0.01, denom = 'ubbn' } +gas_multiplier = 1.5 clock_drift = '1m' # to accomdate docker containers -trusting_period = '239seconds' +trusting_period = '14days' trust_threshold = { numerator = '1', denominator = '3' } [[chains]] +type = "CosmosSdk" id = '$BBN_B_E2E_CHAIN_ID' rpc_addr = 'http://$BBN_B_E2E_VAL_HOST:26657' grpc_addr = 'http://$BBN_B_E2E_VAL_HOST:9090' -websocket_addr = 'ws://$BBN_B_E2E_VAL_HOST:26657/websocket' +event_source = { mode = 'push', url = 'ws://$BBN_B_E2E_VAL_HOST:26657/websocket', batch_delay = '500ms' } rpc_timeout = '10s' account_prefix = 'bbn' key_name = 'val01-bbn-b' store_prefix = 'ibc' -max_gas = 6000000 -gas_price = { price = 0.000, denom = 'ubbn' } -gas_multiplier = 1.1 +max_gas = 50000000 +gas_price = { price = 0.01, denom = 'ubbn' } +gas_multiplier = 1.5 clock_drift = '1m' # to accomdate docker containers -trusting_period = '239seconds' +trusting_period = '14days' trust_threshold = { numerator = '1', denominator = '3' } EOF -# import keys -hermes keys add --chain ${BBN_B_E2E_CHAIN_ID} --key-name "val01-bbn-b" --mnemonic-file /root/.hermes/BBN_B_MNEMONIC.txt -hermes keys add --chain ${BBN_A_E2E_CHAIN_ID} --key-name "val01-bbn-a" --mnemonic-file /root/.hermes/BBN_A_MNEMONIC.txt +# Import keys +hermes keys add --chain ${BBN_A_E2E_CHAIN_ID} --key-name "val01-bbn-a" --mnemonic-file $RELAYER_CONF_DIR/BBN_A_MNEMONIC.txt +hermes keys add --chain ${BBN_B_E2E_CHAIN_ID} --key-name "val01-bbn-b" --mnemonic-file $RELAYER_CONF_DIR/BBN_B_MNEMONIC.txt -# start Hermes relayer +# Start Hermes relayer hermes start diff --git a/test/e2e/scripts/rly_bootstrap.sh b/test/e2e/scripts/rly_bootstrap.sh new file mode 100644 index 000000000..85eb7db10 --- /dev/null +++ b/test/e2e/scripts/rly_bootstrap.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +set -ex + +RELAYER_CONF_DIR=/root/.rly + +# Initialize Cosmos relayer configuration +mkdir -p $RELAYER_CONF_DIR +rly --home $RELAYER_CONF_DIR config init +RELAYER_CONF=$RELAYER_CONF_DIR/config/config.yaml + +# Setup Cosmos relayer configuration +cat <$RELAYER_CONF +global: + api-listen-addr: :5183 + timeout: 20s + memo: "" + light-cache-size: 10 +chains: + bbn-a: + type: cosmos + value: + key-directory: $RELAYER_CONF_DIR/keys/$BBN_A_E2E_CHAIN_ID + key: val01-bbn-a + chain-id: $BBN_A_E2E_CHAIN_ID + rpc-addr: http://$BBN_A_E2E_VAL_HOST:26657 + account-prefix: bbn + keyring-backend: test + gas-adjustment: 1.5 + gas-prices: 0.002ubbn + min-gas-amount: 1 + debug: true + timeout: 10s + output-format: json + sign-mode: direct + extra-codecs: [] + bbn-b: + type: cosmos + value: + key-directory: $RELAYER_CONF_DIR/keys/$BBN_B_E2E_CHAIN_ID + key: val01-bbn-b + chain-id: $BBN_B_E2E_CHAIN_ID + rpc-addr: http://$BBN_B_E2E_VAL_HOST:26657 + account-prefix: bbn + keyring-backend: test + gas-adjustment: 1.5 + gas-prices: 0.002ubbn + min-gas-amount: 1 + debug: true + timeout: 10s + output-format: json + sign-mode: direct + extra-codecs: [] +paths: + bbna-bbnb: + src: + chain-id: $BBN_A_E2E_CHAIN_ID + dst: + chain-id: $BBN_B_E2E_CHAIN_ID +EOF + +# Import keys +rly -d --home $RELAYER_CONF_DIR keys restore bbn-a val01-bbn-a "$BBN_A_E2E_VAL_MNEMONIC" +rly -d --home $RELAYER_CONF_DIR keys restore bbn-b val01-bbn-b "$BBN_B_E2E_VAL_MNEMONIC" +sleep 3 + +# Start Cosmos relayer +echo "Creating IBC light clients, connection, and channel between the two CZs" +rly -d --home $RELAYER_CONF_DIR tx link bbna-bbnb --src-port ${CHAIN_A_IBC_PORT} --dst-port ${CHAIN_B_IBC_PORT} --order ordered --version zoneconcierge-1 +echo "Created IBC channel successfully!" +sleep 10 + +rly -d --home $RELAYER_CONF_DIR start bbna-bbnb --debug-addr "" diff --git a/test/e2e/util/codec.go b/test/e2e/util/codec.go index 73e4bb911..fd633f711 100644 --- a/test/e2e/util/codec.go +++ b/test/e2e/util/codec.go @@ -1,7 +1,7 @@ package util import ( - babylonApp "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/app/params" "github.com/cosmos/cosmos-sdk/codec" @@ -22,7 +22,7 @@ func init() { } func initEncodingConfigAndCdc() (params.EncodingConfig, codec.Codec) { - encodingConfig := babylonApp.GetEncodingConfig() + encodingConfig := app.GetEncodingConfig() encodingConfig.InterfaceRegistry.RegisterImplementations( (*sdk.Msg)(nil), @@ -34,7 +34,7 @@ func initEncodingConfigAndCdc() (params.EncodingConfig, codec.Codec) { &ed25519.PubKey{}, ) - cdc := encodingConfig.Marshaler + cdc := encodingConfig.Codec - return encodingConfig, cdc + return *encodingConfig, cdc } diff --git a/test/integration_test.go b/test/integration_test.go deleted file mode 100644 index c6b4a78bd..000000000 --- a/test/integration_test.go +++ /dev/null @@ -1,375 +0,0 @@ -//go:build integration -// +build integration - -package babylon_integration - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "math/rand" - "os" - "testing" - "time" - - appparams "github.com/babylonchain/babylon/app/params" - txformat "github.com/babylonchain/babylon/btctxformatter" - "github.com/babylonchain/babylon/testutil/datagen" - bbn "github.com/babylonchain/babylon/types" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - lightclient "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/btcsuite/btcd/chaincfg" - ref "github.com/cosmos/cosmos-sdk/client/grpc/reflection" - tm "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - "google.golang.org/grpc" -) - -// Addresses of all nodes in local testnet. -// TODO: instead of hardcoding them it would be nice to get them from env variables -// so docker-compose and this file would stay compatible -var addresses = []string{ - "localhost:9090", - "localhost:9091", - "localhost:9092", - "localhost:9093", -} - -var clients []*grpc.ClientConn - -func checkInterfacesWithRetries(client *grpc.ClientConn, maxTries int, sleepTime time.Duration) error { - tries := 0 - refClient := ref.NewReflectionServiceClient(client) - for { - _, err := refClient.ListAllInterfaces(context.Background(), &ref.ListAllInterfacesRequest{}) - - if err == nil { - // successful call to client, finish calling - return nil - } - - tries++ - - if tries > maxTries { - return errors.New("Failed to call client") - } - - <-time.After(sleepTime) - } -} - -func allClientsOverBlockNumber(clients []*grpc.ClientConn, blockNumber int64) bool { - for _, c := range clients { - latestResponse, err := tm.NewServiceClient(c).GetLatestBlock(context.Background(), &tm.GetLatestBlockRequest{}) - - if err != nil { - errorString := fmt.Sprintf("Integration tests failed, due to node failure. Erro: %v", err) - panic(errorString) - } - - if latestResponse.Block.Header.Height < blockNumber { - return false - } - } - // we iterated over all clients, and all of them were >= blockNumber - return true -} - -func waitForBlock(clients []*grpc.ClientConn, blockNumber int64) { - for { - allOver := allClientsOverBlockNumber(clients, blockNumber) - - if allOver { - return - } - - <-time.After(1 * time.Second) - } -} - -func getCurrentEpoch(conn *grpc.ClientConn) uint64 { - epochingClient := epochingtypes.NewQueryClient(conn) - - currentEpochResponse, err := epochingClient.CurrentEpoch( - context.Background(), - &epochingtypes.QueryCurrentEpochRequest{}, - ) - - if err != nil { - errorString := fmt.Sprintf("Query failed, testnet not running. Error: %v", err) - panic(errorString) - } - - return currentEpochResponse.CurrentEpoch -} - -func TestMain(m *testing.M) { - - // This is needed so that all address prefixes are in Babylon format - appparams.SetAddressPrefixes() - - for _, addr := range addresses { - grpcConn, err := grpc.Dial( - addr, // Or your gRPC server address. - grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism. - ) - - if err != nil { - panic("Grpc connection failed cannot perform integration tests") - } - - clients = append(clients, grpcConn) - } - //runs all following tests - exitVal := m.Run() - - for _, c := range clients { - // close all connections after the tests - c.Close() - } - - os.Exit(exitVal) -} - -// This test serves as a waiting point for testnet to start, it is needed as -// docker compose is usually started in detached mode in CI, therefore tests -// are started even before all nodes are up. -// TODO: investigate starting testnet from golang test file. -func TestTestnetRuninng(t *testing.T) { - - for _, c := range clients { - err := checkInterfacesWithRetries(c, 40, 5*time.Second) - - if err != nil { - panic("Could not start integration tests. Testnet not running") - } - } -} - -// Check all nodes are properly initialized to genesis -// TODO ultimatly we would like to check genesis related to all modules here. -func TestBtcLightClientGenesis(t *testing.T) { - // The default testnet directory uses the simnet genesis header as its base - // with height 0. - hardcodedHeader, _ := bbn.NewBTCHeaderBytesFromHex("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a45068653ffff7f2002000000") - hardcodedHeaderHeight := uint64(0) - - for i, c := range clients { - lc := lightclient.NewQueryClient(c) - - res, err := lc.Tip(context.Background(), lightclient.NewQueryTipRequest()) - - if err != nil { - // this is fatal, as it means we most probably did not get any response and - // at least one of the nodes is down - t.Fatalf("Test failed due to client error: %v to node with address %s", err, addresses[i]) - } - - if res.Header.Height != hardcodedHeaderHeight || !res.Header.Hash.Eq(hardcodedHeader.Hash()) { - t.Errorf("Node with address %s started with unexpected header", addresses[i]) - } - } -} - -func TestNodeProgress(t *testing.T) { - // Waiting for block 7, as tests are configured to run with epoch interval = 5, - // which means that at block 7 all clients will surely be in second epoch or later - waitForBlock(clients, 7) - - for _, c := range clients { - currentEpoch := getCurrentEpoch(c) - if currentEpoch < 2 { - t.Errorf("Epoch after 7 blocks, should be at least larger or equal 2. Current epoch %d", currentEpoch) - } - } -} - -func TestSendTx(t *testing.T) { - r := rand.New(rand.NewSource(time.Now().Unix())) - // TODO fix hard coded paths - node0dataPath := "../.testnets/node0/babylond" - node0genesisPath := "../.testnets/node0/babylond/config/genesis.json" - - sender, err := NewTestTxSender(node0dataPath, node0genesisPath, clients[0]) - - if err != nil { - panic("failed to init sender") - } - tip1 := sender.GetBtcTip() - - err = sender.insertNEmptyBTCHeaders(r, 1) - - if err != nil { - t.Fatalf("could not insert new btc header") - } - - tip2 := sender.GetBtcTip() - - if tip2.Height != tip1.Height+1 { - t.Fatalf("Light client should progress by 1 one block") - } -} - -func TestFailInvalidBTCTransactions(t *testing.T) { - r := rand.New(rand.NewSource(time.Now().Unix())) - // TODO fix hard coded paths - node0dataPath := "../.testnets/node0/babylond" - node0genesisPath := "../.testnets/node0/babylond/config/genesis.json" - - sender, err := NewTestTxSender(node0dataPath, node0genesisPath, clients[0]) - - if err != nil { - panic("failed to init sender") - } - - hInfo := datagen.GenRandomBTCHeaderInfoWithInvalidHeader(r, chaincfg.SimNetParams.PowLimit) - - resp, err := sender.SendBtcHeadersTransaction([]bbn.BTCHeaderBytes{*hInfo.Header}) - - if err != nil { - t.Fatalf("could not insert new btc header") - } - - if resp.TxResponse.Code != 1105 || resp.TxResponse.Codespace != "btclightclient" { - t.Fatalf("submitting invalid header should result with error") - } - - currentTip := sender.GetBtcTip() - - // bogus submissions - firstSubmission := datagen.CreateBlockWithTransaction(r, currentTip.Header.ToBlockHeader(), []byte{1}) - - secondSubmission := datagen.CreateBlockWithTransaction(r, firstSubmission.HeaderBytes.ToBlockHeader(), []byte{1}) - - // At this point light client chain should be 3 long and inserting spv proofs - // should succeed - resp, _ = sender.insertSpvProof(firstSubmission.SpvProof, secondSubmission.SpvProof) - - if resp.TxResponse.Codespace != "btccheckpoint" || resp.TxResponse.Code != 1100 { - t.Fatalf("submitting invalid proof should result with error") - } -} - -func getCheckpoint(t *testing.T, conn *grpc.ClientConn, epoch uint64) *checkpointingtypes.RawCheckpointWithMeta { - queryCheckpoint := checkpointingtypes.NewQueryClient(conn) - - res, err := queryCheckpoint.RawCheckpoint( - context.Background(), - checkpointingtypes.NewQueryRawCheckpointRequest(epoch), - ) - - if err != nil { - t.Fatalf("Failed to retrieve epoch %d", epoch) - } - - return res.RawCheckpoint -} - -func TestSubmitCheckpoint(t *testing.T) { - r := rand.New(rand.NewSource(time.Now().Unix())) - node0dataPath := "../.testnets/node0/babylond" - node0genesisPath := "../.testnets/node0/babylond/config/genesis.json" - - // We are at least on 2 epoch due to `TestNodeProgress` test. At this point - // checkpoint for epoch 1 should already be sealed - testEpoch := uint64(1) - - sender, err := NewTestTxSender(node0dataPath, node0genesisPath, clients[0]) - - if err != nil { - panic("failed to init sender") - } - - rawCheckpoint := getCheckpoint(t, clients[0], testEpoch) - - if rawCheckpoint.Status != checkpointingtypes.Sealed { - t.Fatalf("Expected checkpoint for epoch %d to be Sealed", testEpoch) - } - - rawBtcCheckpoint, err := checkpointingtypes.FromRawCkptToBTCCkpt( - rawCheckpoint.Ckpt, - sender.getSenderAddress().Bytes(), - ) - - if err != nil { - t.Fatalf("Could not create raw btc checkpoint from raw chekpoint") - } - - tagAsBytes, _ := hex.DecodeString(btcctypes.DefaultCheckpointTag) - - p1, p2 := txformat.MustEncodeCheckpointData( - txformat.BabylonTag(tagAsBytes), - txformat.CurrentVersion, - rawBtcCheckpoint, - ) - - currentTip := sender.GetBtcTip() - - firstSubmission := datagen.CreateBlockWithTransaction(r, currentTip.Header.ToBlockHeader(), p1) - - secondSubmission := datagen.CreateBlockWithTransaction(r, firstSubmission.HeaderBytes.ToBlockHeader(), p2) - - // first insert all headers - err = sender.insertBTCHeaders( - currentTip.Height, - []bbn.BTCHeaderBytes{firstSubmission.HeaderBytes, secondSubmission.HeaderBytes}, - ) - - if err != nil { - t.Fatalf("Could not insert two headers. Err: %s", err) - } - - // At this point light client chain should be 3 long and inserting spv proofs - // should succeed - checkPointInsertResponse, err := sender.insertSpvProof(firstSubmission.SpvProof, secondSubmission.SpvProof) - - if err != nil { - t.Log(checkPointInsertResponse.TxResponse) - t.Fatalf("failed to send spv proof") - } - - err = WaitForNextBlock(clients[0]) - - if err != nil { - t.Fatalf("failed to wait for next babylon block") - } - - rawCheckpoint = getCheckpoint(t, clients[0], testEpoch) - - if rawCheckpoint.Status != checkpointingtypes.Submitted { - t.Fatalf("Expected checkpoint for epoch %d to be submitted", testEpoch) - } -} - -func TestConfirmCheckpoint(t *testing.T) { - r := rand.New(rand.NewSource(time.Now().Unix())) - node0dataPath := "../.testnets/node0/babylond" - node0genesisPath := "../.testnets/node0/babylond/config/genesis.json" - - // We are at least on 2 epoch due to `TestNodeProgress` test. At this point - // checkpoint for epoch 1 should already be sealed - testEpoch := uint64(1) - - sender, err := NewTestTxSender(node0dataPath, node0genesisPath, clients[0]) - - if err != nil { - panic("failed to init sender") - } - - err = sender.insertNEmptyBTCHeaders(r, 2) - - if err != nil { - t.Fatalf("Could not insert two headers. Err: %s", err) - } - - // Btc light client chain has been extended by 2 blocks, it means that our checkpoint - // should be confirmed at this point - rawCheckpoint := getCheckpoint(t, clients[0], testEpoch) - - if rawCheckpoint.Status != checkpointingtypes.Confirmed { - t.Fatalf("Expected checkpoint for epoch %d to be confirmed", testEpoch) - } - -} diff --git a/test/utils.go b/test/utils.go deleted file mode 100644 index 4223bc7d2..000000000 --- a/test/utils.go +++ /dev/null @@ -1,385 +0,0 @@ -package babylon_integration - -import ( - "context" - "errors" - "fmt" - "math/rand" - "time" - - tm "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - - "github.com/btcsuite/btcd/wire" - "github.com/cometbft/cometbft/types" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - ctypes "github.com/cosmos/cosmos-sdk/types" - txservice "github.com/cosmos/cosmos-sdk/types/tx" - acctypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "google.golang.org/grpc" - - "github.com/babylonchain/babylon/app" - appparams "github.com/babylonchain/babylon/app/params" - "github.com/babylonchain/babylon/testutil/datagen" - bbn "github.com/babylonchain/babylon/types" - btccheckpoint "github.com/babylonchain/babylon/x/btccheckpoint/types" - lightclient "github.com/babylonchain/babylon/x/btclightclient/types" -) - -type TestTxSender struct { - keyring keyring.Keyring - encConfig appparams.EncodingConfig - signerInfo *keyring.Record - chainId string - Conn *grpc.ClientConn -} - -func NewTestTxSender( - keyringPath string, - genesisPath string, - conn *grpc.ClientConn, -) (*TestTxSender, error) { - cfg := app.GetEncodingConfig() - - kb, err := keyring.New("babylond", "test", keyringPath, nil, cfg.Marshaler) - - if err != nil { - return nil, err - } - - genDoc, err := types.GenesisDocFromFile(genesisPath) - - if err != nil { - return nil, err - } - - signer, err := kb.Key("test-spending-key") - - if err != nil { - panic("test-spending-key should be defined for each node in integration test") - } - - signerInfo := signer - - return &TestTxSender{ - keyring: kb, - encConfig: cfg, - signerInfo: signerInfo, - chainId: genDoc.ChainID, - Conn: conn, - }, nil -} - -func (b *TestTxSender) getSenderAddress() ctypes.AccAddress { - addr, err := b.signerInfo.GetAddress() - - if err != nil { - panic("Getting address from sender should always succeed") - } - return addr -} - -func (b *TestTxSender) buildTx(fees string, gas uint64, seqNr uint64, accNumber uint64, msgs ...ctypes.Msg) []byte { - txFactory := tx.Factory{} - - txFactory = txFactory. - WithKeybase(b.keyring). - WithTxConfig(b.encConfig.TxConfig). - WithChainID(b.chainId). - WithFees(fees). - WithGas(gas). - WithSequence(seqNr). - WithAccountNumber(accNumber) - - txb1, _ := txFactory.BuildUnsignedTx(msgs...) - - if err := tx.Sign(txFactory, b.signerInfo.Name, txb1, true); err != nil { - panic("Tx should sign") - } - - txBytes, err := b.encConfig.TxConfig.TxEncoder()(txb1.GetTx()) - - if err != nil { - panic("Tx should encode") - } - - return txBytes -} - -func (b *TestTxSender) SendBtcHeadersTransaction(headers []bbn.BTCHeaderBytes) (*txservice.BroadcastTxResponse, error) { - if len(headers) == 0 { - return nil, errors.New("headers should not be empty") - } - - acc, err := b.getSelfAccount() - - if err != nil { - panic("retrieving sending account must succeed") - } - - address := b.getSenderAddress() - - var msgs []ctypes.Msg - var fees uint64 - var gas uint64 - - for _, header := range headers { - msg, err := lightclient.NewMsgInsertHeader(address, header.MarshalHex()) - - if err != nil { - panic("creating new header message must succeed ") - } - - msgs = append(msgs, msg) - - fees = fees + 3 - gas = gas + 300000 - } - - feesString := fmt.Sprintf("%d%s", fees, appparams.DefaultBondDenom) - - txBytes := b.buildTx(feesString, gas, acc.GetSequence(), acc.GetAccountNumber(), msgs...) - - req := txservice.BroadcastTxRequest{TxBytes: txBytes, Mode: txservice.BroadcastMode_BROADCAST_MODE_SYNC} - - sender := txservice.NewServiceClient(b.Conn) - - return sender.BroadcastTx(context.Background(), &req) -} - -func GenerateNEmptyHeaders(r *rand.Rand, tip *bbn.BTCHeaderBytes, n uint64) []bbn.BTCHeaderBytes { - var headers []bbn.BTCHeaderBytes - - if n == 0 { - return headers - } - - for i := uint64(0); i < n; i++ { - if i == 0 { - // first new header, need to use tip as base - headers = append(headers, generateEmptyChildHeaderBytes(r, tip)) - } else { - headers = append(headers, generateEmptyChildHeaderBytes(r, &headers[i-1])) - } - } - - return headers -} - -//nolint:unused -func (b *TestTxSender) insertSpvProof(p1 *btccheckpoint.BTCSpvProof, p2 *btccheckpoint.BTCSpvProof) (*txservice.BroadcastTxResponse, error) { - address := b.getSenderAddress() - - msg := btccheckpoint.MsgInsertBTCSpvProof{ - Submitter: address.String(), - Proofs: []*btccheckpoint.BTCSpvProof{p1, p2}, - } - - acc, err := b.getSelfAccount() - - if err != nil { - panic("retrieving sending account must succeed") - } - - fee := fmt.Sprintf("3000%s", appparams.BaseCoinUnit) - txBytes := b.buildTx(fee, 300000, acc.GetSequence(), acc.GetAccountNumber(), &msg) - - req := txservice.BroadcastTxRequest{TxBytes: txBytes, Mode: txservice.BroadcastMode_BROADCAST_MODE_SYNC} - - sender := txservice.NewServiceClient(b.Conn) - - return sender.BroadcastTx(context.Background(), &req) -} - -func (b *TestTxSender) GetBtcTip() *lightclient.BTCHeaderInfo { - lc := lightclient.NewQueryClient(b.Conn) - - res, err := lc.Tip(context.Background(), lightclient.NewQueryTipRequest()) - - if err != nil { - panic("should retrieve btc header") - } - - return res.Header -} - -func (b *TestTxSender) getAccount(addr ctypes.AccAddress) (acctypes.AccountI, error) { - queryClient := acctypes.NewQueryClient(b.Conn) - - res, _ := queryClient.Account( - context.Background(), - &acctypes.QueryAccountRequest{Address: addr.String()}, - ) - - var acc acctypes.AccountI - if err := b.encConfig.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { - return nil, err - } - - return acc, nil -} - -func (b *TestTxSender) getSelfAccount() (acctypes.AccountI, error) { - return b.getAccount(b.getSenderAddress()) -} - -//nolint:unused -func (b *TestTxSender) insertBTCHeaders(currentTip uint64, headers []bbn.BTCHeaderBytes) error { - lenHeaders := len(headers) - - if lenHeaders == 0 { - return nil - } - - _, err := b.SendBtcHeadersTransaction(headers) - - if err != nil { - return err - } - - _, err = WaitBtcForHeight(b.Conn, currentTip+uint64(lenHeaders)) - - if err != nil { - return err - } - - return nil -} - -//nolint:unused -func (b *TestTxSender) insertNEmptyBTCHeaders(r *rand.Rand, n uint64) error { - currentTip := b.GetBtcTip() - headers := GenerateNEmptyHeaders(r, currentTip.Header, n) - - err := b.insertBTCHeaders(currentTip.Height, headers) - - if err != nil { - return err - } - - return nil -} - -func generateEmptyChildHeader(r *rand.Rand, bh *wire.BlockHeader) *wire.BlockHeader { - randHeader := datagen.GenRandomBtcdHeader(r) - - randHeader.Version = bh.Version - randHeader.PrevBlock = bh.BlockHash() - randHeader.Bits = bh.Bits - randHeader.Timestamp = bh.Timestamp.Add(50 * time.Second) - datagen.SolveBlock(randHeader) - - return randHeader -} - -func generateEmptyChildHeaderBytes(r *rand.Rand, bh *bbn.BTCHeaderBytes) bbn.BTCHeaderBytes { - childHeader := generateEmptyChildHeader(r, bh.ToBlockHeader()) - return bbn.NewBTCHeaderBytesFromBlockHeader(childHeader) -} - -// TODO following helpers could probably be generalized by taking function -// as param -// Tendermint blockchain helpers - -func LatestHeight(c *grpc.ClientConn) (int64, error) { - latestResponse, err := tm.NewServiceClient(c).GetLatestBlock(context.Background(), &tm.GetLatestBlockRequest{}) - if err != nil { - return 0, err - } - - return latestResponse.SdkBlock.Header.Height, nil //nolint:staticcheck // deprecated call, suggests to use sdk_block instead -} - -func WaitForHeight(c *grpc.ClientConn, h int64) (int64, error) { - return WaitForHeightWithTimeout(c, h, 15*time.Second) -} - -func WaitForHeightWithTimeout(c *grpc.ClientConn, h int64, t time.Duration) (int64, error) { - ticker := time.NewTicker(time.Second) - timeout := time.After(t) - - var latestHeight int64 - - for { - select { - case <-timeout: - ticker.Stop() - return latestHeight, errors.New("timeout exceeded waiting for block") - case <-ticker.C: - latestH, err := LatestHeight(c) - if err == nil { - latestHeight = latestH - if latestHeight >= h { - return latestHeight, nil - } - } - } - } -} - -func WaitForNextBlock(c *grpc.ClientConn) error { - lastBlock, err := LatestHeight(c) - if err != nil { - return err - } - - _, err = WaitForHeight(c, lastBlock+1) - - if err != nil { - return err - } - - return nil -} - -// Btc blockchain helpers - -func BtcLatestHeight(c *grpc.ClientConn) (uint64, error) { - latestResponse, err := lightclient.NewQueryClient(c).Tip(context.Background(), lightclient.NewQueryTipRequest()) - if err != nil { - return 0, err - } - - return latestResponse.Header.Height, nil -} - -func WaitForBtcHeightWithTimeout(c *grpc.ClientConn, h uint64, t time.Duration) (uint64, error) { - ticker := time.NewTicker(time.Second) - timeout := time.After(t) - var latestHeight uint64 - - for { - select { - case <-timeout: - ticker.Stop() - return latestHeight, errors.New("timeout exceeded waiting for btc block") - case <-ticker.C: - latestH, err := BtcLatestHeight(c) - if err == nil { - latestHeight = latestH - if latestHeight >= h { - return latestHeight, nil - } - } - } - } -} - -func WaitBtcForHeight(c *grpc.ClientConn, h uint64) (uint64, error) { - return WaitForBtcHeightWithTimeout(c, h, 15*time.Second) -} - -func WaitForNextBtcBlock(c *grpc.ClientConn) error { - lastBlock, err := BtcLatestHeight(c) - if err != nil { - return err - } - - _, err = WaitBtcForHeight(c, lastBlock+1) - - if err != nil { - return err - } - - return nil -} diff --git a/testutil/bitcoin/utils.go b/testutil/bitcoin/utils.go new file mode 100644 index 000000000..505a1c1d1 --- /dev/null +++ b/testutil/bitcoin/utils.go @@ -0,0 +1,91 @@ +package bitcoin + +import ( + "bytes" + "fmt" + "testing" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +// Help function to assert the execution of a script engine. Copied from: +// https://github.com/lightningnetwork/lnd/blob/master/input/script_utils_test.go#L24 +func AssertEngineExecution(t *testing.T, testNum int, valid bool, + newEngine func() (*txscript.Engine, error)) { + + t.Helper() + + // Get a new VM to execute. + vm, err := newEngine() + require.NoError(t, err, "unable to create engine") + + // Execute the VM, only go on to the step-by-step execution if + // it doesn't validate as expected. + vmErr := vm.Execute() + if valid == (vmErr == nil) { + return + } + + // Now that the execution didn't match what we expected, fetch a new VM + // to step through. + vm, err = newEngine() + require.NoError(t, err, "unable to create engine") + + // This buffer will trace execution of the Script, dumping out + // to stdout. + var debugBuf bytes.Buffer + + done := false + for !done { + dis, err := vm.DisasmPC() + if err != nil { + t.Fatalf("stepping (%v)\n", err) + } + debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis)) + + done, err = vm.Step() + if err != nil && valid { + t.Log(debugBuf.String()) + t.Fatalf("spend test case #%v failed at (%v), spend "+ + "should be valid: %v", testNum, dis, err) + } else if err == nil && !valid && done { + t.Log(debugBuf.String()) + t.Fatalf("spend test case #%v succeed at (%v), spend "+ + "should be invalid: %v", testNum, dis, err) + } + + debugBuf.WriteString(fmt.Sprintf("Stack: %v\n", vm.GetStack())) + debugBuf.WriteString(fmt.Sprintf("AltStack: %v\n\n", vm.GetAltStack())) + } + + // If we get to this point the unexpected case was not reached + // during step execution, which happens for some checks, like + // the clean-stack rule. + validity := "invalid" + if valid { + validity = "valid" + } + + t.Log(debugBuf.String()) + t.Fatalf("%v spend test case #%v execution ended with: %v", validity, testNum, vmErr) +} + +// AssertSlashingTxExecution asserts that the given tx has a valid witness for spending the given funding output +func AssertSlashingTxExecution(t *testing.T, fundingOutput *wire.TxOut, txWithWitness *wire.MsgTx) { + prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(fundingOutput.PkScript, fundingOutput.Value) + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + fundingOutput.PkScript, + txWithWitness, + 0, + txscript.StandardVerifyFlags, + nil, + txscript.NewTxSigHashes(txWithWitness, prevOutputFetcher), + fundingOutput.Value, + prevOutputFetcher, + ) + } + AssertEngineExecution(t, 0, true, newEngine) +} diff --git a/testutil/datagen/account_balance.go b/testutil/datagen/account_balance.go index e7fd7a4d9..5530ecea5 100644 --- a/testutil/datagen/account_balance.go +++ b/testutil/datagen/account_balance.go @@ -1,12 +1,20 @@ package datagen import ( + sdkmath "cosmossdk.io/math" + appparams "github.com/babylonchain/babylon/app/params" sec256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) +func GenRandomAccount() *authtypes.BaseAccount { + senderPrivKey := sec256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + return acc +} + func GenRandomAccWithBalance(n int) ([]authtypes.GenesisAccount, []banktypes.Balance) { accs := make([]authtypes.GenesisAccount, n) balances := make([]banktypes.Balance, n) @@ -16,7 +24,7 @@ func GenRandomAccWithBalance(n int) ([]authtypes.GenesisAccount, []banktypes.Bal accs[i] = acc balance := banktypes.Balance{ Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdkmath.NewInt(100000000000000))), } balances[i] = balance } diff --git a/testutil/datagen/btc_address.go b/testutil/datagen/btc_address.go new file mode 100644 index 000000000..e7c6589cb --- /dev/null +++ b/testutil/datagen/btc_address.go @@ -0,0 +1,46 @@ +package datagen + +import ( + "io" + "math/rand" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "golang.org/x/crypto/ripemd160" //nolint:all +) + +func GenRandomPkHash(r *rand.Rand) []byte { + md := ripemd160.New() + io.WriteString(md, GenRandomHexStr(r, 20)) //nolint:errcheck + return md.Sum(nil) +} + +func GenRandomBTCAddress(r *rand.Rand, net *chaincfg.Params) (btcutil.Address, error) { + var ( + addr btcutil.Address + err error + ) + + for { + addr, err = btcutil.NewAddressPubKeyHash(GenRandomPkHash(r), net) + if err != nil { + // something is wrong in pkhash or net, return error + return nil, err + } + if _, err := btcutil.DecodeAddress(addr.EncodeAddress(), net); err == nil { + // this is a legit address, use it + break + } + } + + return addr, nil +} + +func GenRandomPubKeyHashScript(r *rand.Rand, net *chaincfg.Params) ([]byte, error) { + addr, err := btcutil.NewAddressPubKeyHash(GenRandomPkHash(r), net) + if err != nil { + return nil, err + } + return txscript.PayToAddrScript(addr) +} diff --git a/testutil/datagen/btc_address_test.go b/testutil/datagen/btc_address_test.go new file mode 100644 index 000000000..b1da6d13e --- /dev/null +++ b/testutil/datagen/btc_address_test.go @@ -0,0 +1,30 @@ +package datagen_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" +) + +func FuzzGenRandomBTCAddress(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + net := &chaincfg.SimNetParams + + addr, err := datagen.GenRandomBTCAddress(r, net) + require.NoError(t, err) + + // validate the address encoding/decoding + addr2, err := btcutil.DecodeAddress(addr.EncodeAddress(), net) + require.NoError(t, err) + + // ensure the address does not change after encoding/decoding + require.Equal(t, addr.String(), addr2.String()) + }) +} diff --git a/testutil/datagen/btc_header_chain.go b/testutil/datagen/btc_header_chain.go new file mode 100644 index 000000000..f2d37ed7f --- /dev/null +++ b/testutil/datagen/btc_header_chain.go @@ -0,0 +1,118 @@ +package datagen + +import ( + "math/rand" + + sdkmath "cosmossdk.io/math" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" +) + +// Init header is always simnet header due to need to solve pow +var initHeader = chaincfg.SimNetParams.GenesisBlock.Header + +type BTCHeaderPartialChain struct { + // slice of Headers forming valid chain + Headers []*wire.BlockHeader + initialHeaderHeight uint64 + inititialHeaderTotalWork sdkmath.Uint +} + +func NewBTCHeaderChainWithLength( + r *rand.Rand, + initialHeaderHeight uint64, + initialHeaderTotalWork uint64, + length uint32) *BTCHeaderPartialChain { + return NewBTCHeaderChainFromParent( + r, + initialHeaderHeight, + sdkmath.NewUint(initialHeaderTotalWork), + &initHeader, + length, + ) +} + +func NewBTCHeaderChainFromParentInfo( + r *rand.Rand, + parent *types.BTCHeaderInfo, + length uint32, +) *BTCHeaderPartialChain { + return NewBTCHeaderChainFromParent( + r, + parent.Height+1, + *parent.Work, + parent.Header.ToBlockHeader(), + length, + ) +} + +func NewBTCHeaderChainFromParent( + r *rand.Rand, + initialHeaderHeight uint64, + initialHeaderTotalWork sdkmath.Uint, + parent *wire.BlockHeader, + length uint32, +) *BTCHeaderPartialChain { + headers := GenRandomValidChainStartingFrom( + r, + initialHeaderHeight, + parent, + nil, + length, + ) + return &BTCHeaderPartialChain{ + Headers: headers, + initialHeaderHeight: initialHeaderHeight, + inititialHeaderTotalWork: initialHeaderTotalWork, + } +} + +func (c *BTCHeaderPartialChain) GetChainInfo() []*types.BTCHeaderInfo { + return ChainToInfoChain(c.Headers, c.initialHeaderHeight, c.inititialHeaderTotalWork) +} + +func (c *BTCHeaderPartialChain) ChainToBytes() []bbn.BTCHeaderBytes { + chainBytes := make([]bbn.BTCHeaderBytes, 0) + for _, header := range c.Headers { + h := header + bytes := bbn.NewBTCHeaderBytesFromBlockHeader(h) + chainBytes = append(chainBytes, bytes) + } + + return chainBytes +} + +func (c *BTCHeaderPartialChain) GetTipInfo() *types.BTCHeaderInfo { + chainInfo := ChainToInfoChain(c.Headers, c.initialHeaderHeight, c.inititialHeaderTotalWork) + return chainInfo[len(chainInfo)-1] +} + +func (c *BTCHeaderPartialChain) TipHeader() *wire.BlockHeader { + return c.Headers[len(c.Headers)-1] +} + +func (c *BTCHeaderPartialChain) GetRandomHeaderInfo(r *rand.Rand) *types.BTCHeaderInfo { + randIdx := RandomInt(r, len(c.Headers)) + headerInfo := ChainToInfoChain(c.Headers, c.initialHeaderHeight, c.inititialHeaderTotalWork) + return headerInfo[randIdx] +} + +func (c *BTCHeaderPartialChain) GetRandomHeaderInfoNoTip(r *rand.Rand) *types.BTCHeaderInfo { + randIdx := RandomInt(r, len(c.Headers)-1) + headerInfo := ChainToInfoChain(c.Headers, c.initialHeaderHeight, c.inititialHeaderTotalWork) + return headerInfo[randIdx] +} + +func (c *BTCHeaderPartialChain) ChainLength() int { + return len(c.Headers) +} + +func (c *BTCHeaderPartialChain) GetHeadersMap() map[string]*wire.BlockHeader { + headersMap := make(map[string]*wire.BlockHeader) + for _, header := range c.Headers { + headersMap[header.BlockHash().String()] = header + } + return headersMap +} diff --git a/testutil/datagen/btc_header_info.go b/testutil/datagen/btc_header_info.go index 4c7d1b378..7cd54ebf9 100644 --- a/testutil/datagen/btc_header_info.go +++ b/testutil/datagen/btc_header_info.go @@ -1,18 +1,68 @@ package datagen import ( + "math/big" + "math/rand" + "time" + sdkmath "cosmossdk.io/math" bbn "github.com/babylonchain/babylon/types" btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - sdk "github.com/cosmos/cosmos-sdk/types" - "math/big" - "math/rand" - "time" ) +type RetargetInfo struct { + LastRetargetHeader *wire.BlockHeader + Params *chaincfg.Params +} + +type TimeBetweenBlocksInfo struct { + Time time.Duration +} + +// Difficulty calculation copied from btcd +// https://github.com/btcsuite/btcd/blob/master/blockchain/difficulty.go#L221 +func calculateAdjustedDifficulty( + lastRetargetHeader *wire.BlockHeader, + currentHeaderTimestamp time.Time, + params *chaincfg.Params) uint32 { + + targetTimespan := int64(params.TargetTimespan / time.Second) + adjustmentFactor := params.RetargetAdjustmentFactor + minRetargetTimespan := targetTimespan / adjustmentFactor + maxRetargetTimespan := targetTimespan * adjustmentFactor + + // Limit the amount of adjustment that can occur to the previous + // difficulty. + actualTimespan := currentHeaderTimestamp.Unix() - lastRetargetHeader.Timestamp.Unix() + adjustedTimespan := actualTimespan + if actualTimespan < minRetargetTimespan { + adjustedTimespan = minRetargetTimespan + } else if actualTimespan > maxRetargetTimespan { + adjustedTimespan = maxRetargetTimespan + } + + // Calculate new target difficulty as: + // currentDifficulty * (adjustedTimespan / targetTimespan) + // The result uses integer division which means it will be slightly + // rounded down. Bitcoind also uses integer division to calculate this + // result. + oldTarget := blockchain.CompactToBig(lastRetargetHeader.Bits) + newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan)) + newTarget.Div(newTarget, big.NewInt(targetTimespan)) + + // Limit new value to the proof of work limit. + if newTarget.Cmp(params.PowLimit) > 0 { + newTarget.Set(params.PowLimit) + } + + newTargetBits := blockchain.BigToCompact(newTarget) + return newTargetBits +} + func GenRandomBtcdHeader(r *rand.Rand) *wire.BlockHeader { version := GenRandomBTCHeaderVersion(r) bits := GenRandomBTCHeaderBits(r) @@ -42,7 +92,7 @@ func GenRandomBTCHeaderBits(r *rand.Rand) uint32 { if difficulty == 0 { difficulty += 1 } - bigDifficulty := sdk.NewUint(difficulty) + bigDifficulty := sdkmath.NewUint(difficulty) workBits := blockchain.BigToCompact(bigDifficulty.BigInt()) return workBits @@ -145,15 +195,8 @@ func GenRandomBTCHeaderInfoWithParent(r *rand.Rand, parent *btclightclienttypes. // WARNING: if parent is from network with a lot of work (mainnet) it may never finish // use only with simnet headers func GenRandomValidBTCHeaderInfoWithParent(r *rand.Rand, parent btclightclienttypes.BTCHeaderInfo) *btclightclienttypes.BTCHeaderInfo { - randHeader := GenRandomBtcdHeader(r) parentHeader := parent.Header.ToBlockHeader() - - randHeader.Version = parentHeader.Version - randHeader.PrevBlock = parentHeader.BlockHash() - randHeader.Bits = parentHeader.Bits - randHeader.Timestamp = parentHeader.Timestamp.Add(50 * time.Second) - SolveBlock(randHeader) - + randHeader := GenRandomBtcdValidHeader(r, parentHeader, nil, nil) headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(randHeader) accumulatedWork := btclightclienttypes.CalcWork(&headerBytes) @@ -167,6 +210,110 @@ func GenRandomValidBTCHeaderInfoWithParent(r *rand.Rand, parent btclightclientty } } +// random duration between 4 and 12mins in seconds +func GenRandomTimeBetweenBlocks(r *rand.Rand) time.Duration { + return time.Duration(r.Int63n(8*60)+4*60) * time.Second +} + +func GenRandomBtcdValidHeader( + r *rand.Rand, + parent *wire.BlockHeader, + timeAfterParent *TimeBetweenBlocksInfo, + retargetInfo *RetargetInfo, +) *wire.BlockHeader { + randHeader := GenRandomBtcdHeader(r) + randHeader.Version = 4 + randHeader.PrevBlock = parent.BlockHash() + + if timeAfterParent == nil { + // random time after + randHeader.Timestamp = parent.Timestamp.Add(GenRandomTimeBetweenBlocks(r)) + } else { + randHeader.Timestamp = parent.Timestamp.Add(timeAfterParent.Time) + } + + if retargetInfo == nil { + // If no retarget info is provided, then we assume that the difficulty is the same as the parent + randHeader.Bits = parent.Bits + } else { + // If retarget info is provided, then we calculate the difficulty based on the info provided + randHeader.Bits = calculateAdjustedDifficulty( + retargetInfo.LastRetargetHeader, + parent.Timestamp, + retargetInfo.Params, + ) + } + SolveBlock(randHeader) + return randHeader +} + +func GenRandomValidChainStartingFrom( + r *rand.Rand, + parentHeaderHeight uint64, + parentHeader *wire.BlockHeader, + timeBetweenBlocks *TimeBetweenBlocksInfo, + numHeaders uint32, +) []*wire.BlockHeader { + if numHeaders == 0 { + return []*wire.BlockHeader{} + } + + headers := make([]*wire.BlockHeader, numHeaders) + for i := uint32(0); i < numHeaders; i++ { + if i == 0 { + headers[i] = GenRandomBtcdValidHeader(r, parentHeader, timeBetweenBlocks, nil) + continue + } + + headers[i] = GenRandomBtcdValidHeader(r, headers[i-1], timeBetweenBlocks, nil) + } + return headers +} + +func ChainToInfoChain( + chain []*wire.BlockHeader, + initialHeaderNumber uint64, + initialHeaderTotalWork sdkmath.Uint, +) []*btclightclienttypes.BTCHeaderInfo { + if len(chain) == 0 { + return []*btclightclienttypes.BTCHeaderInfo{} + } + + infoChain := make([]*btclightclienttypes.BTCHeaderInfo, len(chain)) + + totalDifficulty := initialHeaderTotalWork + + for i, header := range chain { + headerWork := btclightclienttypes.CalcHeaderWork(header) + headerTotalDifficulty := btclightclienttypes.CumulativeWork(headerWork, totalDifficulty) + hash := header.BlockHash() + headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(header) + headerHash := bbn.NewBTCHeaderHashBytesFromChainhash(&hash) + headerNumber := initialHeaderNumber + uint64(i) + + headerInfo := btclightclienttypes.NewBTCHeaderInfo( + &headerBytes, + &headerHash, + headerNumber, + &headerTotalDifficulty, + ) + + infoChain[i] = headerInfo + + totalDifficulty = headerTotalDifficulty + } + + return infoChain +} + +func HeaderToHeaderBytes(headers []*wire.BlockHeader) []bbn.BTCHeaderBytes { + headerBytes := make([]bbn.BTCHeaderBytes, len(headers)) + for i, header := range headers { + headerBytes[i] = bbn.NewBTCHeaderBytesFromBlockHeader(header) + } + return headerBytes +} + func GenRandomBTCHeaderInfoWithBits(r *rand.Rand, bits *sdkmath.Uint) *btclightclienttypes.BTCHeaderInfo { return GenRandomBTCHeaderInfoWithParentAndBits(r, nil, bits) } diff --git a/testutil/datagen/btc_header_tree.go b/testutil/datagen/btc_header_tree.go deleted file mode 100644 index 43151da0c..000000000 --- a/testutil/datagen/btc_header_tree.go +++ /dev/null @@ -1,257 +0,0 @@ -package datagen - -import ( - blctypes "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "math/rand" -) - -type BTCHeaderTree struct { - // headers is a map of unique identifies to BTCHeaderInfo objects - headers map[string]*blctypes.BTCHeaderInfo - // children is a map of unique identifies to unique identifiers for children - children map[string][]string -} - -func NewBTCHeaderTree() *BTCHeaderTree { - headers := make(map[string]*blctypes.BTCHeaderInfo, 0) - children := make(map[string][]string, 0) - return &BTCHeaderTree{headers: headers, children: children} -} - -// Add adds a node into storage. If the `parent` is set, -// it is also added to the list of `parent`. -func (t *BTCHeaderTree) Add(node *blctypes.BTCHeaderInfo, parent *blctypes.BTCHeaderInfo) { - t.headers[node.Hash.String()] = node - if parent != nil { - t.children[parent.Hash.String()] = append(t.children[parent.Hash.String()], node.Hash.String()) - } -} - -// Contains checks whether a node is maintained in the internal storage -func (t *BTCHeaderTree) Contains(node *blctypes.BTCHeaderInfo) bool { - if _, ok := t.headers[node.Hash.String()]; ok { - return true - } - return false -} - -// GetRoot returns the root of the tree -- i.e. the node without an existing parent -func (t *BTCHeaderTree) GetRoot() *blctypes.BTCHeaderInfo { - for _, header := range t.headers { - if t.GetParent(header) == nil { - return header - } - } - return nil -} - -// GetTip returns the header in the tree with the most work -func (t *BTCHeaderTree) GetTip() *blctypes.BTCHeaderInfo { - maxWork := sdk.NewUint(0) - var tip *blctypes.BTCHeaderInfo - for _, node := range t.headers { - if node.Work.GT(maxWork) { - maxWork = *node.Work - tip = node - } - } - return tip -} - -// GetMainChain returns the tree fork with the most work -func (t *BTCHeaderTree) GetMainChain() []*blctypes.BTCHeaderInfo { - tip := t.GetTip() - return t.GetNodeAncestry(tip) -} - -// RandNumChildren randomly generates 0-2 children with the following probabilities: -// If zeroChildrenAllowed is not set: -// -// 1 child: 75% -// 2 children: 25% -// -// Otherwise, -// -// 0 children: 25% -// 1 child: 50% -// 2 children: 25% -func (t *BTCHeaderTree) RandNumChildren(r *rand.Rand, zeroChildrenAllowed bool) int { - // Randomly identify the number of children - numChildren := 0 - // If the flag is not set, then we need to generate a child for sure - if !zeroChildrenAllowed { - numChildren = 1 // 75% chance of 1 child now - } - if OneInN(r, 2) { - // 50% of the times, one child - numChildren = 1 - } else if OneInN(r, 2) { - // 25% of the times, 2 children - // Implies that 25% of the times 0 children - numChildren = 2 - } - return numChildren -} - -// GenRandomBTCHeaderTree recursively generates a random tree of BTCHeaderInfo objects rooted at `parent`. -// The tree generation is accomplished by randomly selecting the number of children using the `RandNumChildren()`. -// Then, for each child, a random BTCHeaderInfo object is generated and a new tree rooted -// at that child is recursively generated. -// For each node that is generated, the callback function is invoked in order to identify -// whether we should continue generating or not as well as help with maintenance -// tasks (e.g. inserting headers into keeper storage). -func (t *BTCHeaderTree) GenRandomBTCHeaderTree(r *rand.Rand, minHeight uint64, maxHeight uint64, - parent *blctypes.BTCHeaderInfo, callback func(info *blctypes.BTCHeaderInfo) bool) { - - if maxHeight == 0 { - // If we generate more, we exceed the maximum height - return - } - - const maxRetries = 3 - retries := 0 - // Generate the children of the parent - for i := 0; i < t.RandNumChildren(r, minHeight <= 1); i++ { - childInfo := GenRandomBTCHeaderInfoWithParent(r, parent) - - // Rare occasion that we get the same hash, skip - if t.Contains(childInfo) { - // Only retry up to 3 times to generate the child - if retries < maxRetries { - i -= 1 - } - retries += 1 - continue - } - - // Only generate `minHeight-1` subtrees for the first child - childMinHeight := uint64(0) - if i == 0 && minHeight-1 > 0 { - childMinHeight = minHeight - 1 - } - if callback(childInfo) { - t.Add(childInfo, parent) - t.GenRandomBTCHeaderTree(r, childMinHeight, maxHeight-1, childInfo, callback) - } - } -} - -// RandomNode selects a random header from the list of nodes -func (t *BTCHeaderTree) RandomNode(r *rand.Rand) *blctypes.BTCHeaderInfo { - randIdx := RandomInt(r, len(t.headers)) - var idx uint64 = 0 - for _, node := range t.headers { - if idx == randIdx { - return node - } - idx += 1 - } - return nil -} - -// getNodeAncestryUpToUtil recursively iterates the parents of the node until the root node is reached -func (t *BTCHeaderTree) getNodeAncestryUpToUtil(ancestry *[]*blctypes.BTCHeaderInfo, - node *blctypes.BTCHeaderInfo, upTo *blctypes.BTCHeaderInfo) { - - if upTo != nil && node.Eq(upTo) { - return - } - *ancestry = append(*ancestry, node) - parent := t.GetParent(node) - if parent != nil { - t.getNodeAncestryUpToUtil(ancestry, parent, upTo) - } -} - -// GetNodeAncestryUpTo returns an ancestry list starting from the tree node and -// leading to a child of the `upTo` parameter if it is not nil. -func (t *BTCHeaderTree) GetNodeAncestryUpTo(node *blctypes.BTCHeaderInfo, - upTo *blctypes.BTCHeaderInfo) []*blctypes.BTCHeaderInfo { - - ancestry := make([]*blctypes.BTCHeaderInfo, 0) - t.getNodeAncestryUpToUtil(&ancestry, node, upTo) - return ancestry -} - -// GetNodeAncestry returns an ancestry list starting from the tree node and -// leading to the root of the tree. -func (t *BTCHeaderTree) GetNodeAncestry(node *blctypes.BTCHeaderInfo) []*blctypes.BTCHeaderInfo { - return t.GetNodeAncestryUpTo(node, nil) -} - -// RandomAncestor retrieves the ancestry list and returns an ancestor from it. -// Can include the node itself. -func (t *BTCHeaderTree) RandomAncestor(r *rand.Rand, node *blctypes.BTCHeaderInfo) *blctypes.BTCHeaderInfo { - ancestry := t.GetNodeAncestry(node) - idx := RandomInt(r, len(ancestry)) - return ancestry[idx] -} - -// IsOnNodeChain returns true or false depending on whether the node -// is equal or a descendant of the `ancestor` parameter. -func (t *BTCHeaderTree) IsOnNodeChain(node *blctypes.BTCHeaderInfo, ancestor *blctypes.BTCHeaderInfo) bool { - if node.Eq(ancestor) { - return true - } - ancestryUpTo := t.GetNodeAncestryUpTo(node, ancestor) - lastElement := ancestryUpTo[len(ancestryUpTo)-1] - parent := t.GetParent(lastElement) - if parent != nil && parent.Eq(ancestor) { - return true - } - return false -} - -// GetChildren returns the children of a node as a list of BTCHeaderInfo objects -func (t *BTCHeaderTree) GetChildren(node *blctypes.BTCHeaderInfo) []*blctypes.BTCHeaderInfo { - if !t.Contains(node) { - panic("Retrieving children of non existent node") - } - childrenHash := t.children[node.Hash.String()] - children := make([]*blctypes.BTCHeaderInfo, 0) - for _, childHash := range childrenHash { - children = append(children, t.headers[childHash]) - } - return children -} - -// getNodeDescendantsUtil recursively iterates the descendants of a node and adds them to a list -func (t *BTCHeaderTree) getNodeDescendantsUtil(descendants *[]*blctypes.BTCHeaderInfo, node *blctypes.BTCHeaderInfo) { - *descendants = append(*descendants, node) - for _, child := range t.GetChildren(node) { - t.getNodeDescendantsUtil(descendants, child) - } -} - -// GetNodeDescendants returns a list of the descendants of a node -func (t *BTCHeaderTree) GetNodeDescendants(node *blctypes.BTCHeaderInfo) []*blctypes.BTCHeaderInfo { - descendants := make([]*blctypes.BTCHeaderInfo, 0) - t.getNodeDescendantsUtil(&descendants, node) - return descendants -} - -// RandomDescendant returns a random descendant of the node -func (t *BTCHeaderTree) RandomDescendant(r *rand.Rand, node *blctypes.BTCHeaderInfo) *blctypes.BTCHeaderInfo { - descendants := t.GetNodeDescendants(node) - idx := RandomInt(r, len(descendants)) - return descendants[idx] -} - -// GetHeadersMap returns a mapping between node hashes and nodes -func (t *BTCHeaderTree) GetHeadersMap() map[string]*blctypes.BTCHeaderInfo { - return t.headers -} - -// Size returns the number of nodes that are maintained -func (t *BTCHeaderTree) Size() int { - return len(t.headers) -} - -// GetParent returns the parent of the node, or nil if it doesn't exist -func (t *BTCHeaderTree) GetParent(node *blctypes.BTCHeaderInfo) *blctypes.BTCHeaderInfo { - if header, ok := t.headers[node.Header.ParentHash().String()]; ok { - return header - } - return nil -} diff --git a/testutil/datagen/btc_schnorr.go b/testutil/datagen/btc_schnorr.go new file mode 100644 index 000000000..1eec8d047 --- /dev/null +++ b/testutil/datagen/btc_schnorr.go @@ -0,0 +1,56 @@ +package datagen + +import ( + "math/rand" + + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +func GenRandomBTCKeyPair(r *rand.Rand) (*btcec.PrivateKey, *btcec.PublicKey, error) { + sk, err := secp256k1.GeneratePrivateKeyFromRand(r) + if err != nil { + return nil, nil, err + } + return sk, sk.PubKey(), nil +} + +func GenRandomBTCKeyPairs(r *rand.Rand, n int) ([]*btcec.PrivateKey, []*btcec.PublicKey, error) { + sks, pks := []*btcec.PrivateKey{}, []*btcec.PublicKey{} + for i := 0; i < n; i++ { + sk, pk, err := GenRandomBTCKeyPair(r) + if err != nil { + return nil, nil, err + } + sks = append(sks, sk) + pks = append(pks, pk) + } + + return sks, pks, nil +} + +func GenRandomBIP340PubKey(r *rand.Rand) (*bbn.BIP340PubKey, error) { + sk, err := secp256k1.GeneratePrivateKeyFromRand(r) + if err != nil { + return nil, err + } + pk := sk.PubKey() + btcPK := bbn.NewBIP340PubKeyFromBTCPK(pk) + return btcPK, nil +} + +// GenCovenantCommittee generates a covenant committee +// with random number of members and quorum size +func GenCovenantCommittee(r *rand.Rand) ([]*btcec.PrivateKey, []*btcec.PublicKey, uint32) { + committeeSize := RandomInt(r, 5) + 5 + quorumSize := uint32(committeeSize/2 + 1) + sks, pks := []*btcec.PrivateKey{}, []*btcec.PublicKey{} + for i := uint64(0); i < committeeSize; i++ { + skBytes := GenRandomByteArray(r, 32) + sk, pk := btcec.PrivKeyFromBytes(skBytes) + sks = append(sks, sk) + pks = append(pks, pk) + } + return sks, pks, quorumSize +} diff --git a/testutil/datagen/btc_transaction.go b/testutil/datagen/btc_transaction.go index b9e86c45a..9bcdad7f9 100644 --- a/testutil/datagen/btc_transaction.go +++ b/testutil/datagen/btc_transaction.go @@ -9,11 +9,6 @@ import ( "runtime" "time" - txformat "github.com/babylonchain/babylon/btctxformatter" - - "github.com/babylonchain/babylon/btctxformatter" - bbn "github.com/babylonchain/babylon/types" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" @@ -21,6 +16,10 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" + + txformat "github.com/babylonchain/babylon/btctxformatter" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" ) var ( @@ -35,7 +34,7 @@ var ( type testCheckpointData struct { epoch uint64 - lastCommitHash []byte + blockHash []byte bitmap []byte blsSig []byte submitterAddress []byte @@ -303,16 +302,17 @@ type BtcHeaderWithProof struct { func CreateBlockWithTransaction( r *rand.Rand, ph *wire.BlockHeader, - babylonData []byte, + tx *wire.MsgTx, ) *BtcHeaderWithProof { var transactions []*wire.MsgTx // height does not matter here, as it is used only for calculation of reward transactions = append(transactions, createCoinbaseTx(int32(889), &chaincfg.SimNetParams)) - transactions = append(transactions, CreatOpReturnTransaction(r, babylonData)) + transactions = append(transactions, tx) randHeader := GenRandomBtcdHeader(r) - randHeader.Version = ph.Version + // for simnet, requirement for block versions to be >= 4, is enabled from initial block + randHeader.Version = 4 randHeader.PrevBlock = ph.BlockHash() randHeader.Bits = ph.Bits randHeader.Timestamp = ph.Timestamp.Add(50 * time.Second) @@ -366,7 +366,7 @@ func GenRandomTx(r *rand.Rand) *wire.MsgTx { return tx } -func GenRandomBabylonTxPair(r *rand.Rand) ([]*wire.MsgTx, *btctxformatter.RawBtcCheckpoint) { +func GenRandomBabylonTxPair(r *rand.Rand) ([]*wire.MsgTx, *txformat.RawBtcCheckpoint) { txs := []*wire.MsgTx{GenRandomTx(r), GenRandomTx(r)} builder := txscript.NewScriptBuilder() @@ -374,9 +374,9 @@ func GenRandomBabylonTxPair(r *rand.Rand) ([]*wire.MsgTx, *btctxformatter.RawBtc rawBTCCkpt := GetRandomRawBtcCheckpoint(r) tag := GenRandomByteArray(r, 4) // encode raw checkpoint to two halves - firstHalf, secondHalf, err := btctxformatter.EncodeCheckpointData( - btctxformatter.BabylonTag(tag), - btctxformatter.CurrentVersion, + firstHalf, secondHalf, err := txformat.EncodeCheckpointData( + txformat.BabylonTag(tag), + txformat.CurrentVersion, rawBTCCkpt, ) if err != nil { @@ -459,7 +459,7 @@ func GenerateMessageWithRandomSubmitter(blockResults []*BlockCreationResult) *bt func getRandomCheckpointDataForEpoch(r *rand.Rand, e uint64) testCheckpointData { return testCheckpointData{ epoch: e, - lastCommitHash: GenRandomByteArray(r, txformat.LastCommitHashLength), + blockHash: GenRandomByteArray(r, txformat.BlockHashLength), bitmap: GenRandomByteArray(r, txformat.BitMapLength), blsSig: GenRandomByteArray(r, txformat.BlsSigLength), submitterAddress: GenRandomByteArray(r, txformat.AddressLength), @@ -503,7 +503,7 @@ func RandomRawCheckpointDataForEpoch(r *rand.Rand, e uint64) (*TestRawCheckpoint checkpointData := getRandomCheckpointDataForEpoch(r, e) rawBTCCkpt := &txformat.RawBtcCheckpoint{ Epoch: checkpointData.epoch, - LastCommitHash: checkpointData.lastCommitHash, + BlockHash: checkpointData.blockHash, BitMap: checkpointData.bitmap, SubmitterAddress: checkpointData.submitterAddress, BlsSig: checkpointData.blsSig, diff --git a/testutil/datagen/btcstaking.go b/testutil/datagen/btcstaking.go new file mode 100644 index 000000000..9bcdcba52 --- /dev/null +++ b/testutil/datagen/btcstaking.go @@ -0,0 +1,467 @@ +package datagen + +import ( + "fmt" + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/btcstaking" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" +) + +const ( + StakingOutIdx = uint32(0) + UnbondingTxFee = int64(1000) +) + +func GenRandomFinalityProvider(r *rand.Rand) (*bstypes.FinalityProvider, error) { + // key pairs + btcSK, _, err := GenRandomBTCKeyPair(r) + if err != nil { + return nil, err + } + return GenRandomFinalityProviderWithBTCSK(r, btcSK) +} + +func CreateNFinalityProviders(r *rand.Rand, t *testing.T, n int) []*bstypes.FinalityProvider { + fps := make([]*bstypes.FinalityProvider, n) + for i := 0; i < n; i++ { + fp, err := GenRandomFinalityProvider(r) + require.NoError(t, err) + fps[i] = fp + } + return fps +} + +func GenRandomFinalityProviderWithBTCSK(r *rand.Rand, btcSK *btcec.PrivateKey) (*bstypes.FinalityProvider, error) { + bbnSK, _, err := GenRandomSecp256k1KeyPair(r) + if err != nil { + return nil, err + } + return GenRandomFinalityProviderWithBTCBabylonSKs(r, btcSK, bbnSK) +} + +func GenRandomFinalityProviderWithBTCBabylonSKs(r *rand.Rand, btcSK *btcec.PrivateKey, bbnSK cryptotypes.PrivKey) (*bstypes.FinalityProvider, error) { + // commission + commission := sdkmath.LegacyNewDecWithPrec(int64(RandomInt(r, 49)+1), 2) // [1/100, 50/100] + // description + description := stakingtypes.Description{Moniker: GenRandomHexStr(r, 10)} + // key pairs + btcPK := btcSK.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + bbnPK := bbnSK.PubKey() + secp256k1PK, ok := bbnPK.(*secp256k1.PubKey) + if !ok { + return nil, fmt.Errorf("failed to assert bbnPK to *secp256k1.PubKey") + } + // pop + pop, err := bstypes.NewPoP(bbnSK, btcSK) + if err != nil { + return nil, err + } + return &bstypes.FinalityProvider{ + Description: &description, + Commission: &commission, + BabylonPk: secp256k1PK, + BtcPk: bip340PK, + Pop: pop, + }, nil +} + +// TODO: accomodate presign unbonding flow +func GenRandomBTCDelegation( + r *rand.Rand, + t *testing.T, + fpBTCPKs []bbn.BIP340PubKey, + delSK *btcec.PrivateKey, + covenantSKs []*btcec.PrivateKey, + covenantQuorum uint32, + slashingAddress string, + startHeight, endHeight, totalSat uint64, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) (*bstypes.BTCDelegation, error) { + net := &chaincfg.SimNetParams + delPK := delSK.PubKey() + delBTCPK := bbn.NewBIP340PubKeyFromBTCPK(delPK) + // list of covenant PKs + covenantBTCPKs := []*btcec.PublicKey{} + for _, covenantSK := range covenantSKs { + covenantBTCPKs = append(covenantBTCPKs, covenantSK.PubKey()) + } + // list of finality provider PKs + fpPKs := []*btcec.PublicKey{} + for _, fpBTCPK := range fpBTCPKs { + fpPK, err := fpBTCPK.ToBTCPK() + if err != nil { + return nil, err + } + fpPKs = append(fpPKs, fpPK) + } + + // BTC delegation Babylon key pairs + bbnSK, bbnPK, err := GenRandomSecp256k1KeyPair(r) + if err != nil { + return nil, err + } + secp256k1PK, ok := bbnPK.(*secp256k1.PubKey) + if !ok { + return nil, fmt.Errorf("failed to assert bbnPK to *secp256k1.PubKey") + } + // pop + pop, err := bstypes.NewPoP(bbnSK, delSK) + if err != nil { + return nil, err + } + // staking/slashing tx + stakingSlashingInfo := GenBTCStakingSlashingInfo( + r, + t, + net, + delSK, + fpPKs, + covenantBTCPKs, + covenantQuorum, + uint16(endHeight-startHeight), + int64(totalSat), + slashingAddress, + slashingRate, + slashingChangeLockTime, + ) + + slashingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + stakingMsgTx := stakingSlashingInfo.StakingTx + + // delegator sig + delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( + stakingMsgTx, + StakingOutIdx, + slashingPathSpendInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + // covenant sigs + covenantSigs, err := GenCovenantAdaptorSigs( + covenantSKs, + fpPKs, + stakingMsgTx, + slashingPathSpendInfo.GetPkScriptPath(), + stakingSlashingInfo.SlashingTx, + ) + require.NoError(t, err) + + serializedStakingTx, err := bbn.SerializeBTCTx(stakingSlashingInfo.StakingTx) + require.NoError(t, err) + w := uint16(100) // TODO: parameterise w + del := &bstypes.BTCDelegation{ + BabylonPk: secp256k1PK, + BtcPk: delBTCPK, + Pop: pop, + FpBtcPkList: fpBTCPKs, + StartHeight: startHeight, + EndHeight: endHeight, + TotalSat: totalSat, + StakingOutputIdx: StakingOutIdx, + DelegatorSig: delegatorSig, + CovenantSigs: covenantSigs, + UnbondingTime: uint32(w + 1), + StakingTx: serializedStakingTx, + SlashingTx: stakingSlashingInfo.SlashingTx, + } + + /* + construct BTC undelegation + */ + + // construct unbonding info + stkTxHash := stakingSlashingInfo.StakingTx.TxHash() + unbondingValue := totalSat - uint64(UnbondingTxFee) + + unbondingSlashingInfo := GenBTCUnbondingSlashingInfo( + r, + t, + net, + delSK, + fpPKs, + covenantBTCPKs, + covenantQuorum, + wire.NewOutPoint(&stkTxHash, StakingOutIdx), + w+1, + int64(unbondingValue), + slashingAddress, + slashingRate, + slashingChangeLockTime, + ) + + unbondingTxBytes, err := bbn.SerializeBTCTx(unbondingSlashingInfo.UnbondingTx) + require.NoError(t, err) + delSlashingTxSig, err := unbondingSlashingInfo.GenDelSlashingTxSig(delSK) + require.NoError(t, err) + del.BtcUndelegation = &bstypes.BTCUndelegation{ + UnbondingTx: unbondingTxBytes, + SlashingTx: unbondingSlashingInfo.SlashingTx, + DelegatorSlashingSig: delSlashingTxSig, + } + + /* + covenant signs BTC undelegation + */ + + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + + covUnbondingSlashingSigs, covUnbondingSigs, err := unbondingSlashingInfo.GenCovenantSigs( + covenantSKs, + fpPKs, + stakingMsgTx, + unbondingPathSpendInfo.GetPkScriptPath(), + ) + require.NoError(t, err) + + del.BtcUndelegation.CovenantSlashingSigs = covUnbondingSlashingSigs + del.BtcUndelegation.CovenantUnbondingSigList = covUnbondingSigs + + return del, nil +} + +type TestStakingSlashingInfo struct { + StakingTx *wire.MsgTx + SlashingTx *bstypes.BTCSlashingTx + StakingInfo *btcstaking.StakingInfo +} + +type TestUnbondingSlashingInfo struct { + UnbondingTx *wire.MsgTx + SlashingTx *bstypes.BTCSlashingTx + UnbondingInfo *btcstaking.UnbondingInfo +} + +func GenBTCStakingSlashingInfoWithOutPoint( + r *rand.Rand, + t testing.TB, + btcNet *chaincfg.Params, + outPoint *wire.OutPoint, + stakerSK *btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + covenantPKs []*btcec.PublicKey, + covenantQuorum uint32, + stakingTimeBlocks uint16, + stakingValue int64, + slashingAddress string, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) *TestStakingSlashingInfo { + + stakingInfo, err := btcstaking.BuildStakingInfo( + stakerSK.PubKey(), + fpPKs, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + btcutil.Amount(stakingValue), + btcNet, + ) + + require.NoError(t, err) + tx := wire.NewMsgTx(2) + // add the given tx input + txIn := wire.NewTxIn(outPoint, nil, nil) + tx.AddTxIn(txIn) + tx.AddTxOut(stakingInfo.StakingOutput) + + // 2 outputs for changes and staking output + changeAddrScript, err := GenRandomPubKeyHashScript(r, btcNet) + require.NoError(t, err) + require.False(t, txscript.GetScriptClass(changeAddrScript) == txscript.NonStandardTy) + + tx.AddTxOut(wire.NewTxOut(10000, changeAddrScript)) // output for change + + // construct slashing tx + slashingAddrBtc, err := btcutil.DecodeAddress(slashingAddress, btcNet) + require.NoError(t, err) + + slashingMsgTx, err := btcstaking.BuildSlashingTxFromStakingTxStrict( + tx, + StakingOutIdx, + slashingAddrBtc, + stakerSK.PubKey(), + slashingChangeLockTime, + 2000, + slashingRate, + btcNet) + require.NoError(t, err) + slashingTx, err := bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) + require.NoError(t, err) + + return &TestStakingSlashingInfo{ + StakingTx: tx, + SlashingTx: slashingTx, + StakingInfo: stakingInfo, + } +} + +func GenBTCStakingSlashingInfo( + r *rand.Rand, + t testing.TB, + btcNet *chaincfg.Params, + stakerSK *btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + covenantPKs []*btcec.PublicKey, + covenantQuorum uint32, + stakingTimeBlocks uint16, + stakingValue int64, + slashingAddress string, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) *TestStakingSlashingInfo { + // an arbitrary input + spend := makeSpendableOutWithRandOutPoint(r, btcutil.Amount(stakingValue+UnbondingTxFee)) + outPoint := &spend.prevOut + return GenBTCStakingSlashingInfoWithOutPoint( + r, + t, + btcNet, + outPoint, + stakerSK, + fpPKs, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + stakingValue, + slashingAddress, + slashingRate, + slashingChangeLockTime, + ) +} + +func GenBTCUnbondingSlashingInfo( + r *rand.Rand, + t testing.TB, + btcNet *chaincfg.Params, + stakerSK *btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + covenantPKs []*btcec.PublicKey, + covenantQuorum uint32, + stakingTransactionOutpoint *wire.OutPoint, + stakingTimeBlocks uint16, + stakingValue int64, + slashingAddress string, + slashingRate sdkmath.LegacyDec, + slashingChangeLockTime uint16, +) *TestUnbondingSlashingInfo { + + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + stakerSK.PubKey(), + fpPKs, + covenantPKs, + covenantQuorum, + slashingChangeLockTime, + btcutil.Amount(stakingValue), + btcNet, + ) + + require.NoError(t, err) + tx := wire.NewMsgTx(2) + // add the given tx input + txIn := wire.NewTxIn(stakingTransactionOutpoint, nil, nil) + tx.AddTxIn(txIn) + tx.AddTxOut(unbondingInfo.UnbondingOutput) + + // construct slashing tx + slashingAddrBtc, err := btcutil.DecodeAddress(slashingAddress, btcNet) + require.NoError(t, err) + + slashingMsgTx, err := btcstaking.BuildSlashingTxFromStakingTxStrict( + tx, + StakingOutIdx, + slashingAddrBtc, + stakerSK.PubKey(), + slashingChangeLockTime, + 2000, + slashingRate, + btcNet) + require.NoError(t, err) + slashingTx, err := bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) + require.NoError(t, err) + + return &TestUnbondingSlashingInfo{ + UnbondingTx: tx, + SlashingTx: slashingTx, + UnbondingInfo: unbondingInfo, + } +} + +func (info *TestUnbondingSlashingInfo) GenDelSlashingTxSig(sk *btcec.PrivateKey) (*bbn.BIP340Signature, error) { + unbondingTxMsg := info.UnbondingTx + unbondingTxSlashingPathInfo, err := info.UnbondingInfo.SlashingPathSpendInfo() + if err != nil { + return nil, err + } + slashingTxSig, err := info.SlashingTx.Sign( + unbondingTxMsg, + StakingOutIdx, + unbondingTxSlashingPathInfo.GetPkScriptPath(), + sk, + ) + if err != nil { + return nil, err + } + return slashingTxSig, nil +} + +func (info *TestUnbondingSlashingInfo) GenCovenantSigs( + covSKs []*btcec.PrivateKey, + fpPKs []*btcec.PublicKey, + stakingTx *wire.MsgTx, + unbondingPkScriptPath []byte, +) ([]*bstypes.CovenantAdaptorSignatures, []*bstypes.SignatureInfo, error) { + unbondingSlashingPathInfo, err := info.UnbondingInfo.SlashingPathSpendInfo() + if err != nil { + return nil, nil, err + } + + covUnbondingSlashingSigs, err := GenCovenantAdaptorSigs( + covSKs, + fpPKs, + info.UnbondingTx, + unbondingSlashingPathInfo.GetPkScriptPath(), + info.SlashingTx, + ) + if err != nil { + return nil, nil, err + } + covUnbondingSigs, err := GenCovenantUnbondingSigs( + covSKs, + stakingTx, + StakingOutIdx, + unbondingPkScriptPath, + info.UnbondingTx, + ) + if err != nil { + return nil, nil, err + } + covUnbondingSigList := []*bstypes.SignatureInfo{} + for i := range covUnbondingSigs { + covUnbondingSigList = append(covUnbondingSigList, &bstypes.SignatureInfo{ + Pk: bbn.NewBIP340PubKeyFromBTCPK(covSKs[i].PubKey()), + Sig: bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), + }) + } + return covUnbondingSlashingSigs, covUnbondingSigList, nil +} diff --git a/testutil/datagen/covenant.go b/testutil/datagen/covenant.go new file mode 100644 index 000000000..ae2fd835b --- /dev/null +++ b/testutil/datagen/covenant.go @@ -0,0 +1,59 @@ +package datagen + +import ( + "github.com/babylonchain/babylon/btcstaking" + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/wire" +) + +func GenCovenantAdaptorSigs( + covenantSKs []*btcec.PrivateKey, + valPKs []*btcec.PublicKey, + fundingTx *wire.MsgTx, + pkScriptPath []byte, + slashingTx *bstypes.BTCSlashingTx, +) ([]*bstypes.CovenantAdaptorSignatures, error) { + covenantSigs := []*bstypes.CovenantAdaptorSignatures{} + for _, covenantSK := range covenantSKs { + covMemberSigs := &bstypes.CovenantAdaptorSignatures{ + CovPk: bbn.NewBIP340PubKeyFromBTCPK(covenantSK.PubKey()), + AdaptorSigs: [][]byte{}, + } + for _, valPK := range valPKs { + encKey, err := asig.NewEncryptionKeyFromBTCPK(valPK) + if err != nil { + return nil, err + } + covenantSig, err := slashingTx.EncSign(fundingTx, 0, pkScriptPath, covenantSK, encKey) + if err != nil { + return nil, err + } + covMemberSigs.AdaptorSigs = append(covMemberSigs.AdaptorSigs, covenantSig.MustMarshal()) + } + covenantSigs = append(covenantSigs, covMemberSigs) + } + + return covenantSigs, nil +} + +func GenCovenantUnbondingSigs(covenantSKs []*btcec.PrivateKey, stakingTx *wire.MsgTx, stakingOutIdx uint32, unbondingPkScriptPath []byte, unbondingTx *wire.MsgTx) ([]*schnorr.Signature, error) { + sigs := []*schnorr.Signature{} + for i := range covenantSKs { + sig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingTx, + stakingTx, + stakingOutIdx, + unbondingPkScriptPath, + covenantSKs[i], + ) + if err != nil { + return nil, err + } + sigs = append(sigs, sig) + } + return sigs, nil +} diff --git a/testutil/datagen/epoching.go b/testutil/datagen/epoching.go index 4b44d4c69..6cec642f1 100644 --- a/testutil/datagen/epoching.go +++ b/testutil/datagen/epoching.go @@ -35,8 +35,10 @@ func GenRandomEpoch(r *rand.Rand) *epochingtypes.Epoch { epochNum, epochInterval, firstBlockHeight, - lastBlockHeader, + &lastBlockHeader.Time, ) - epoch.SealerHeader = GenRandomTMHeader(r, "test-chain", firstBlockHeight+epochInterval+1) // 2nd block in the next epoch + sealerHeader := GenRandomTMHeader(r, "test-chain", firstBlockHeight+epochInterval+1) // 2nd block in the next epoch + epoch.SealerBlockHash = GenRandomBlockHash(r) + epoch.SealerAppHash = sealerHeader.AppHash return &epoch } diff --git a/testutil/datagen/finality.go b/testutil/datagen/finality.go new file mode 100644 index 000000000..73eae543b --- /dev/null +++ b/testutil/datagen/finality.go @@ -0,0 +1,81 @@ +package datagen + +import ( + "math/rand" + + "github.com/babylonchain/babylon/crypto/eots" + bbn "github.com/babylonchain/babylon/types" + ftypes "github.com/babylonchain/babylon/x/finality/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func GenRandomPubRandList(r *rand.Rand, numPubRand uint64) ([]*eots.PrivateRand, []bbn.SchnorrPubRand, error) { + srList := []*eots.PrivateRand{} + prList := []bbn.SchnorrPubRand{} + for i := uint64(0); i < numPubRand; i++ { + eotsSR, eotsPR, err := eots.RandGen(r) + if err != nil { + return nil, nil, err + } + pr := bbn.NewSchnorrPubRandFromFieldVal(eotsPR) + srList = append(srList, eotsSR) + prList = append(prList, *pr) + } + return srList, prList, nil +} + +func GenRandomMsgCommitPubRandList(r *rand.Rand, sk *btcec.PrivateKey, startHeight uint64, numPubRand uint64) ([]*eots.PrivateRand, *ftypes.MsgCommitPubRandList, error) { + srList, prList, err := GenRandomPubRandList(r, numPubRand) + if err != nil { + return nil, nil, err + } + + msg := &ftypes.MsgCommitPubRandList{ + Signer: GenRandomAccount().Address, + FpBtcPk: bbn.NewBIP340PubKeyFromBTCPK(sk.PubKey()), + StartHeight: startHeight, + PubRandList: prList, + } + hash, err := msg.HashToSign() + if err != nil { + return nil, nil, err + } + schnorrSig, err := schnorr.Sign(sk, hash) + if err != nil { + return nil, nil, err + } + msg.Sig = bbn.NewBIP340SignatureFromBTCSig(schnorrSig) + return srList, msg, nil +} + +func GenRandomEvidence(r *rand.Rand, sk *btcec.PrivateKey, height uint64) (*ftypes.Evidence, error) { + pk := sk.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(pk) + sr, pr, err := eots.RandGen(r) + if err != nil { + return nil, err + } + cAppHash := GenRandomByteArray(r, 32) + cSig, err := eots.Sign(sk, sr, append(sdk.Uint64ToBigEndian(height), cAppHash...)) + if err != nil { + return nil, err + } + fAppHash := GenRandomByteArray(r, 32) + fSig, err := eots.Sign(sk, sr, append(sdk.Uint64ToBigEndian(height), fAppHash...)) + if err != nil { + return nil, err + } + + evidence := &ftypes.Evidence{ + FpBtcPk: bip340PK, + BlockHeight: height, + PubRand: bbn.NewSchnorrPubRandFromFieldVal(pr), + CanonicalAppHash: cAppHash, + ForkAppHash: fAppHash, + CanonicalFinalitySig: bbn.NewSchnorrEOTSSigFromModNScalar(cSig), + ForkFinalitySig: bbn.NewSchnorrEOTSSigFromModNScalar(fSig), + } + return evidence, nil +} diff --git a/testutil/datagen/genesiskey.go b/testutil/datagen/genesiskey.go index 89e2ee6ff..3b15e14ad 100644 --- a/testutil/datagen/genesiskey.go +++ b/testutil/datagen/genesiskey.go @@ -1,21 +1,122 @@ package datagen import ( - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/privval" - "github.com/babylonchain/babylon/x/checkpointing/types" - ed255192 "github.com/cometbft/cometbft/crypto/ed25519" + cmtcrypto "github.com/cometbft/cometbft/crypto" + cmted25519 "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/privval" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" ) -func GenerateGenesisKey() *types.GenesisKey { +type GenesisValidators struct { + Keys []*GenesisKeyWithBLS +} + +type GenesisKeyWithBLS struct { + checkpointingtypes.GenesisKey + bls12381.PrivateKey + cmtcrypto.PrivKey +} + +func (gvs *GenesisValidators) GetGenesisKeys() []*checkpointingtypes.GenesisKey { + gensisKeys := make([]*checkpointingtypes.GenesisKey, 0, len(gvs.Keys)) + for _, k := range gvs.Keys { + gensisKeys = append(gensisKeys, &k.GenesisKey) + } + + return gensisKeys +} + +func (gvs *GenesisValidators) GetBLSPrivKeys() []bls12381.PrivateKey { + blsPrivKeys := make([]bls12381.PrivateKey, 0, len(gvs.Keys)) + for _, k := range gvs.Keys { + blsPrivKeys = append(blsPrivKeys, k.PrivateKey) + } + + return blsPrivKeys +} + +func (gvs *GenesisValidators) GetValPrivKeys() []cmtcrypto.PrivKey { + valPrivKeys := make([]cmtcrypto.PrivKey, 0, len(gvs.Keys)) + for _, k := range gvs.Keys { + valPrivKeys = append(valPrivKeys, k.PrivKey) + } + + return valPrivKeys +} + +// GenesisValidatorSet generates a set with `numVals` genesis validators +func GenesisValidatorSet(numVals int) (*GenesisValidators, error) { + genesisVals := make([]*GenesisKeyWithBLS, 0, numVals) + for i := 0; i < numVals; i++ { + blsPrivKey := bls12381.GenPrivKey() + // create validator set with single validator + valPrivKey := cmted25519.GenPrivKey() + valKeys, err := privval.NewValidatorKeys(valPrivKey, blsPrivKey) + if err != nil { + return nil, err + } + valPubkey, err := cryptocodec.FromCmtPubKeyInterface(valKeys.ValPubkey) + if err != nil { + return nil, err + } + genesisKey, err := checkpointingtypes.NewGenesisKey( + sdk.ValAddress(valKeys.ValPubkey.Address()), + &valKeys.BlsPubkey, + valKeys.PoP, + &cosmosed.PubKey{Key: valPubkey.Bytes()}, + ) + if err != nil { + return nil, err + } + genesisVals = append(genesisVals, &GenesisKeyWithBLS{ + GenesisKey: *genesisKey, + PrivateKey: blsPrivKey, + PrivKey: valPrivKey, + }) + } + + return &GenesisValidators{Keys: genesisVals}, nil +} + +// GenesisValidatorSetWithPrivSigner generates a set with `numVals` genesis validators +// along with the privSigner, which will be in the 0th position of the return validator set +func GenesisValidatorSetWithPrivSigner(numVals int) (*GenesisValidators, *app.PrivSigner, error) { + ps, err := app.SetupTestPrivSigner() + if err != nil { + return nil, nil, err + } + signerGenesisKey, err := app.GenesisKeyFromPrivSigner(ps) + if err != nil { + return nil, nil, err + } + signerVal := &GenesisKeyWithBLS{ + GenesisKey: *signerGenesisKey, + PrivateKey: ps.WrappedPV.Key.BlsPrivKey, + PrivKey: ps.WrappedPV.Key.PrivKey, + } + genesisVals, err := GenesisValidatorSet(numVals) + if err != nil { + return nil, nil, err + } + genesisVals.Keys[0] = signerVal + + return genesisVals, ps, nil +} + +func GenerateGenesisKey() *checkpointingtypes.GenesisKey { accPrivKey := secp256k1.GenPrivKey() - tmValPrivKey := ed255192.GenPrivKey() + tmValPrivKey := cmted25519.GenPrivKey() blsPrivKey := bls12381.GenPrivKey() tmValPubKey := tmValPrivKey.PubKey() - valPubKey, err := codec.FromTmPubKeyInterface(tmValPubKey) + valPubKey, err := codec.FromCmtPubKeyInterface(tmValPubKey) if err != nil { panic(err) } @@ -27,7 +128,7 @@ func GenerateGenesisKey() *types.GenesisKey { panic(err) } - gk, err := types.NewGenesisKey(address, &blsPubKey, pop, valPubKey) + gk, err := checkpointingtypes.NewGenesisKey(address, &blsPubKey, pop, valPubKey) if err != nil { panic(err) } diff --git a/testutil/datagen/incentive.go b/testutil/datagen/incentive.go new file mode 100644 index 000000000..327a746e0 --- /dev/null +++ b/testutil/datagen/incentive.go @@ -0,0 +1,134 @@ +package datagen + +import ( + "math/rand" + + sdkmath "cosmossdk.io/math" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + itypes "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + denomLen = 5 +) + +func GenRandomDenom(r *rand.Rand) string { + var result string + // Generate the random string + for i := 0; i < denomLen; i++ { + // Generate a random index within the range of the character set + index := r.Intn(len(characters)) + // Add the randomly selected character to the result + result += string(characters[index]) + } + return result +} + +func GenRandomStakeholderType(r *rand.Rand) itypes.StakeholderType { + stBytes := []byte{byte(RandomInt(r, 4))} + st, err := itypes.NewStakeHolderType(stBytes) + if err != nil { + panic(err) // only programming error is possible + } + return st +} + +func GenRandomCoins(r *rand.Rand) sdk.Coins { + numCoins := r.Int31n(10) + 10 + coins := sdk.NewCoins() + for i := int32(0); i < numCoins; i++ { + demon := GenRandomDenom(r) + amount := r.Int63n(10000) + 1 + coin := sdk.NewInt64Coin(demon, amount) + coins = coins.Add(coin) + } + return coins +} + +func GenRandomRewardGauge(r *rand.Rand) *itypes.RewardGauge { + coins := GenRandomCoins(r) + return itypes.NewRewardGauge(coins...) +} + +func GenRandomWithdrawnCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins { + withdrawnCoins := sdk.NewCoins() + for _, coin := range coins { + // skip this coin with some probability + if OneInN(r, 3) { + continue + } + // a subset of the coin has been withdrawn + withdrawnAmount := coin.Amount.Uint64() + if withdrawnAmount > 1 { + withdrawnAmount = RandomInt(r, int(withdrawnAmount)-1) + 1 + } + withdrawnCoin := sdk.NewCoin(coin.Denom, sdkmath.NewIntFromUint64(withdrawnAmount)) + withdrawnCoins = withdrawnCoins.Add(withdrawnCoin) + } + return withdrawnCoins +} + +func GenRandomGauge(r *rand.Rand) *itypes.Gauge { + coins := GenRandomCoins(r) + return itypes.NewGauge(coins...) +} + +func GenRandomBTCDelDistInfo(r *rand.Rand) *bstypes.BTCDelDistInfo { + return &bstypes.BTCDelDistInfo{ + BabylonPk: GenRandomAccount().GetPubKey().(*secp256k1.PubKey), + VotingPower: RandomInt(r, 1000) + 1, + } +} + +func GenRandomFinalityProviderDistInfo(r *rand.Rand) (*bstypes.FinalityProviderDistInfo, error) { + // create finality provider with random commission + fp, err := GenRandomFinalityProvider(r) + if err != nil { + return nil, err + } + // create finality provider distribution info + fpDistInfo := bstypes.NewFinalityProviderDistInfo(fp) + // add a random number of BTC delegation distribution info + numBTCDels := RandomInt(r, 100) + 1 + for i := uint64(0); i < numBTCDels; i++ { + btcDelDistInfo := GenRandomBTCDelDistInfo(r) + fpDistInfo.BtcDels = append(fpDistInfo.BtcDels, btcDelDistInfo) + fpDistInfo.TotalVotingPower += btcDelDistInfo.VotingPower + } + return fpDistInfo, nil +} + +func GenRandomBTCStakingRewardDistCache(r *rand.Rand) (*bstypes.RewardDistCache, error) { + rdc := bstypes.NewRewardDistCache() + // a random number of finality providers + numFps := RandomInt(r, 10) + 1 + for i := uint64(0); i < numFps; i++ { + v, err := GenRandomFinalityProviderDistInfo(r) + if err != nil { + return nil, err + } + rdc.AddFinalityProviderDistInfo(v) + } + return rdc, nil +} + +func GenRandomCheckpointAddressPair(r *rand.Rand) *btcctypes.CheckpointAddressPair { + return &btcctypes.CheckpointAddressPair{ + Submitter: GenRandomAccount().GetAddress(), + Reporter: GenRandomAccount().GetAddress(), + } +} + +func GenRandomBTCTimestampingRewardDistInfo(r *rand.Rand) *btcctypes.RewardDistInfo { + best := GenRandomCheckpointAddressPair(r) + numOthers := RandomInt(r, 10) + others := []*btcctypes.CheckpointAddressPair{} + for i := uint64(0); i < numOthers; i++ { + others = append(others, GenRandomCheckpointAddressPair(r)) + } + return btcctypes.NewRewardDistInfo(best, others...) +} diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index 6b06d2e6e..5870ca8cf 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -2,15 +2,17 @@ package datagen import ( "fmt" - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/privval" + "path/filepath" + cfg "github.com/cometbft/cometbft/config" - tmed25519 "github.com/cometbft/cometbft/crypto/ed25519" - tmos "github.com/cometbft/cometbft/libs/os" + cmted25519 "github.com/cometbft/cometbft/crypto/ed25519" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/p2p" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/go-bip39" - "path/filepath" + + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/privval" ) // InitializeNodeValidatorFiles creates private validator and p2p configuration files. @@ -31,12 +33,12 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin nodeID = string(nodeKey.ID()) pvKeyFile := config.PrivValidatorKeyFile() - if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { + if err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { return "", nil, err } pvStateFile := config.PrivValidatorStateFile() - if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { + if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { return "", nil, err } @@ -44,7 +46,7 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin if len(mnemonic) == 0 { filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) } else { - privKey := tmed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + privKey := cmted25519.GenPrivKeyFromSecret([]byte(mnemonic)) blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) filePV = privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) } diff --git a/testutil/datagen/priv_validator.go b/testutil/datagen/priv_validator.go index 42af77a70..cccfbec88 100644 --- a/testutil/datagen/priv_validator.go +++ b/testutil/datagen/priv_validator.go @@ -2,9 +2,8 @@ package datagen import ( "github.com/cometbft/cometbft/crypto" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" - + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -13,7 +12,7 @@ import ( // adapted from https://github.com/ClanNetwork/clan-network/blob/d00b1cc465/testutil/simapp/pv.go // used for creating key pairs for genesis validators in tests -var _ tmtypes.PrivValidator = PV{} +var _ cmttypes.PrivValidator = PV{} // PV implements PrivValidator without any safety or persistence. // Only use it for testing. @@ -27,12 +26,12 @@ func NewPV() PV { // GetPubKey implements PrivValidator interface func (pv PV) GetPubKey() (crypto.PubKey, error) { - return cryptocodec.ToTmPubKeyInterface(pv.PrivKey.PubKey()) + return cryptocodec.ToCmtPubKeyInterface(pv.PrivKey.PubKey()) } // SignVote implements PrivValidator interface -func (pv PV) SignVote(chainID string, vote *tmproto.Vote) error { - signBytes := tmtypes.VoteSignBytes(chainID, vote) +func (pv PV) SignVote(chainID string, vote *cmtproto.Vote) error { + signBytes := cmttypes.VoteSignBytes(chainID, vote) sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err @@ -42,8 +41,8 @@ func (pv PV) SignVote(chainID string, vote *tmproto.Vote) error { } // SignProposal implements PrivValidator interface -func (pv PV) SignProposal(chainID string, proposal *tmproto.Proposal) error { - signBytes := tmtypes.ProposalSignBytes(chainID, proposal) +func (pv PV) SignProposal(chainID string, proposal *cmtproto.Proposal) error { + signBytes := cmttypes.ProposalSignBytes(chainID, proposal) sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err diff --git a/testutil/datagen/raw_checkpoint.go b/testutil/datagen/raw_checkpoint.go index 37cb771bf..88512ab0f 100644 --- a/testutil/datagen/raw_checkpoint.go +++ b/testutil/datagen/raw_checkpoint.go @@ -5,19 +5,31 @@ import ( "github.com/boljen/go-bitmap" - "github.com/babylonchain/babylon/btctxformatter" txformat "github.com/babylonchain/babylon/btctxformatter" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/x/checkpointing/types" ) +const ( + numValidators = 100 // BTC timestamping only supports 100 validators +) + +func GenFullBitmap() bitmap.Bitmap { + bmBytes := make([]byte, txformat.BitMapLength) + bm := bitmap.Bitmap(bmBytes) + for i := 0; i < numValidators; i++ { + bitmap.Set(bm, i, true) + } + return bm +} + // GenRandomBitmap generates a random bitmap for the validator set // It returns a random bitmap and the number of validators in the subset func GenRandomBitmap(r *rand.Rand) (bitmap.Bitmap, int) { bmBytes := GenRandomByteArray(r, txformat.BitMapLength) bm := bitmap.Bitmap(bmBytes) numSubset := 0 - for i := 0; i < bm.Len(); i++ { + for i := 0; i < numValidators; i++ { if bitmap.Get(bm, i) { numSubset++ } @@ -25,13 +37,13 @@ func GenRandomBitmap(r *rand.Rand) (bitmap.Bitmap, int) { return bm, numSubset } -func GetRandomRawBtcCheckpoint(r *rand.Rand) *btctxformatter.RawBtcCheckpoint { +func GetRandomRawBtcCheckpoint(r *rand.Rand) *txformat.RawBtcCheckpoint { rawCkpt := GenRandomRawCheckpoint(r) - return &btctxformatter.RawBtcCheckpoint{ + return &txformat.RawBtcCheckpoint{ Epoch: rawCkpt.EpochNum, - LastCommitHash: *rawCkpt.LastCommitHash, + BlockHash: *rawCkpt.BlockHash, BitMap: rawCkpt.Bitmap, - SubmitterAddress: GenRandomByteArray(r, btctxformatter.AddressLength), + SubmitterAddress: GenRandomByteArray(r, txformat.AddressLength), BlsSig: rawCkpt.BlsMultiSig.Bytes(), } } @@ -46,13 +58,13 @@ func GenRandomRawCheckpointWithMeta(r *rand.Rand) *types.RawCheckpointWithMeta { } func GenRandomRawCheckpoint(r *rand.Rand) *types.RawCheckpoint { - randomHashBytes := GenRandomLastCommitHash(r) + randomHashBytes := GenRandomBlockHash(r) randomBLSSig := GenRandomBlsMultiSig(r) return &types.RawCheckpoint{ - EpochNum: GenRandomEpochNum(r), - LastCommitHash: &randomHashBytes, - Bitmap: bitmap.New(types.BitmapBits), - BlsMultiSig: &randomBLSSig, + EpochNum: GenRandomEpochNum(r), + BlockHash: &randomHashBytes, + Bitmap: bitmap.New(types.BitmapBits), + BlsMultiSig: &randomBLSSig, } } @@ -108,10 +120,10 @@ func GenerateLegitimateRawCheckpoint(r *rand.Rand, privKeys []bls12381.PrivateKe // number of validators, at least 4 n := len(privKeys) // ensure sufficient signers - signerNum := n/3 + 1 + signerNum := n*2/3 + 1 epochNum := GenRandomEpochNum(r) - lch := GenRandomLastCommitHash(r) - msgBytes := types.GetSignBytes(epochNum, lch) + blockHash := GenRandomBlockHash(r) + msgBytes := types.GetSignBytes(epochNum, blockHash) sigs := GenerateBLSSigs(privKeys[:signerNum], msgBytes) multiSig, _ := bls12381.AggrSigList(sigs) bm := bitmap.New(types.BitmapBits) @@ -119,16 +131,16 @@ func GenerateLegitimateRawCheckpoint(r *rand.Rand, privKeys []bls12381.PrivateKe bm.Set(i, true) } btcCheckpoint := &types.RawCheckpoint{ - EpochNum: epochNum, - LastCommitHash: &lch, - Bitmap: bm, - BlsMultiSig: &multiSig, + EpochNum: epochNum, + BlockHash: &blockHash, + Bitmap: bm, + BlsMultiSig: &multiSig, } return btcCheckpoint } -func GenRandomLastCommitHash(r *rand.Rand) types.LastCommitHash { +func GenRandomBlockHash(r *rand.Rand) types.BlockHash { return GenRandomByteArray(r, types.HashSize) } diff --git a/testutil/datagen/secp256k1.go b/testutil/datagen/secp256k1.go new file mode 100644 index 000000000..3db651e22 --- /dev/null +++ b/testutil/datagen/secp256k1.go @@ -0,0 +1,15 @@ +package datagen + +import ( + "math/rand" + + secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +func GenRandomSecp256k1KeyPair(r *rand.Rand) (cryptotypes.PrivKey, cryptotypes.PubKey, error) { + randBytes := GenRandomByteArray(r, 10) + sk := secp256k1.GenPrivKeyFromSecret(randBytes) + pk := sk.PubKey() + return sk, pk, nil +} diff --git a/testutil/datagen/tendermint.go b/testutil/datagen/tendermint.go index 928bb3f4f..cea24c2e5 100644 --- a/testutil/datagen/tendermint.go +++ b/testutil/datagen/tendermint.go @@ -4,36 +4,58 @@ import ( "math/rand" "time" - extendedkeeper "github.com/babylonchain/babylon/x/zoneconcierge/extended-client-keeper" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "cosmossdk.io/core/header" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + + zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" ) -func GenRandomTMHeader(r *rand.Rand, chainID string, height uint64) *tmproto.Header { - return &tmproto.Header{ - ChainID: chainID, - Height: int64(height), - Time: time.Now(), - LastCommitHash: GenRandomByteArray(r, 32), +func GenRandomTMHeader(r *rand.Rand, chainID string, height uint64) *cmtproto.Header { + return &cmtproto.Header{ + ChainID: chainID, + Height: int64(height), + Time: time.Now(), + AppHash: GenRandomByteArray(r, 32), } } func GenRandomIBCTMHeader(r *rand.Rand, chainID string, height uint64) *ibctmtypes.Header { return &ibctmtypes.Header{ - SignedHeader: &tmproto.SignedHeader{ - Header: &tmproto.Header{ - ChainID: chainID, - Height: int64(height), - LastCommitHash: GenRandomByteArray(r, 32), + SignedHeader: &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ChainID: chainID, + Height: int64(height), + AppHash: GenRandomByteArray(r, 32), }, }, } } -func HeaderToHeaderInfo(header *ibctmtypes.Header) *extendedkeeper.HeaderInfo { - return &extendedkeeper.HeaderInfo{ - Hash: header.Header.LastCommitHash, - ChaindId: header.Header.ChainID, - Height: uint64(header.Header.Height), +func GenRandomTMHeaderInfo(r *rand.Rand, chainID string, height uint64) *header.Info { + tmHeader := GenRandomIBCTMHeader(r, chainID, height) + return &header.Info{ + Height: tmHeader.Header.Height, + Hash: tmHeader.Header.DataHash, + Time: tmHeader.Header.Time, + ChainID: tmHeader.Header.ChainID, + AppHash: tmHeader.Header.AppHash, + } +} + +func HeaderToHeaderInfo(header *ibctmtypes.Header) *zctypes.HeaderInfo { + return &zctypes.HeaderInfo{ + AppHash: header.Header.AppHash, + ChainId: header.Header.ChainID, + Time: header.Header.Time, + Height: uint64(header.Header.Height), } } + +func WithCtxHeight(ctx sdk.Context, height uint64) sdk.Context { + headerInfo := ctx.HeaderInfo() + headerInfo.Height = int64(height) + ctx = ctx.WithHeaderInfo(headerInfo) + return ctx +} diff --git a/testutil/datagen/vote_ext.go b/testutil/datagen/vote_ext.go new file mode 100644 index 000000000..6b880a133 --- /dev/null +++ b/testutil/datagen/vote_ext.go @@ -0,0 +1,37 @@ +package datagen + +import ( + "math/rand" + + abci "github.com/cometbft/cometbft/abci/types" + + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" +) + +func GenRandomVoteExtension( + epochNum, height uint64, + blockHash checkpointingtypes.BlockHash, + valSet *GenesisValidators, + r *rand.Rand, +) ([]abci.ExtendedVoteInfo, error) { + genesisKeys := valSet.GetGenesisKeys() + extendedVotes := make([]abci.ExtendedVoteInfo, 0, len(valSet.Keys)) + for i := 0; i < len(valSet.Keys); i++ { + sig := GenRandomBlsMultiSig(r) + ve := checkpointingtypes.VoteExtension{ + Signer: genesisKeys[i].ValidatorAddress, + BlockHash: blockHash, + EpochNum: epochNum, + Height: height, + BlsSig: &sig, + } + veBytes, err := ve.Marshal() + if err != nil { + return nil, err + } + veInfo := abci.ExtendedVoteInfo{VoteExtension: veBytes} + extendedVotes = append(extendedVotes, veInfo) + } + + return extendedVotes, nil +} diff --git a/testutil/helper/README.md b/testutil/helper/README.md new file mode 100644 index 000000000..8968fde79 --- /dev/null +++ b/testutil/helper/README.md @@ -0,0 +1,3 @@ +# helper + +The code under this directory provides infrastructure for testing the Babylon app. The code is adapted from https://github.com/cosmos/cosmos-sdk/tree/v0.45.5/x/staking/teststaking \ No newline at end of file diff --git a/testutil/helper/gen_blocks.go b/testutil/helper/gen_blocks.go new file mode 100644 index 000000000..d2f9d721f --- /dev/null +++ b/testutil/helper/gen_blocks.go @@ -0,0 +1,339 @@ +package helper + +import ( + "fmt" + "math/rand" + + "cosmossdk.io/core/header" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/merkle" + cmttypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/babylonchain/babylon/testutil/datagen" +) + +func (h *Helper) genAndApplyEmptyBlock() error { + prevHeight := h.App.LastBlockHeight() + newHeight := prevHeight + 1 + + // finalize block + valSet, err := h.App.StakingKeeper.GetLastValidators(h.Ctx) + if err != nil { + return err + } + valhash := CalculateValHash(valSet) + newHeader := cmttypes.Header{ + Height: newHeight, + ValidatorsHash: valhash, + NextValidatorsHash: valhash, + } + + resp, err := h.App.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: newHeader.Height, + NextValidatorsHash: newHeader.NextValidatorsHash, + Hash: newHeader.Hash(), + }) + if err != nil { + return err + } + + newHeader.AppHash = resp.AppHash + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + AppHash: resp.AppHash, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + _, err = h.App.Commit() + if err != nil { + return err + } + + if newHeight == 1 { + // do it again + // TODO: Figure out why when ctx height is 1, ApplyEmptyBlockWithVoteExtension + // will still give ctx height 1 once, then start to increment + return h.genAndApplyEmptyBlock() + } + + return nil +} + +func (h *Helper) ApplyEmptyBlockWithVoteExtension(r *rand.Rand) (sdk.Context, error) { + emptyCtx := sdk.Context{} + if h.App.LastBlockHeight() == 0 { + if err := h.genAndApplyEmptyBlock(); err != nil { + return emptyCtx, err + } + } + valSetWithKeys := h.GenValidators + prevHeight := h.App.LastBlockHeight() + epoch := h.App.EpochingKeeper.GetEpoch(h.Ctx) + newHeight := prevHeight + 1 + + // 1. get previous vote extensions + prevEpoch := epoch.EpochNumber + blockHash := datagen.GenRandomBlockHash(r) + extendedVotes, err := h.getExtendedVotesFromValSet(prevEpoch, uint64(prevHeight), blockHash, valSetWithKeys) + if err != nil { + return emptyCtx, err + } + + // 2. create new header + valSet, err := h.App.StakingKeeper.GetLastValidators(h.Ctx) + if err != nil { + return emptyCtx, err + } + valhash := CalculateValHash(valSet) + newHeader := cmttypes.Header{ + Height: newHeight, + ValidatorsHash: valhash, + NextValidatorsHash: valhash, + LastBlockID: cmttypes.BlockID{ + Hash: datagen.GenRandomByteArray(r, 32), + }, + } + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + // 3. prepare proposal with previous BLS sigs + blockTxs := [][]byte{} + ppRes, err := h.App.PrepareProposal(&abci.RequestPrepareProposal{ + LocalLastCommit: abci.ExtendedCommitInfo{Votes: extendedVotes}, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + if len(ppRes.Txs) > 0 { + blockTxs = ppRes.Txs + } + _, err = h.App.ProcessProposal(&abci.RequestProcessProposal{ + Txs: ppRes.Txs, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + // 4. finalize block + resp, err := h.App.FinalizeBlock(&abci.RequestFinalizeBlock{ + Txs: blockTxs, + Height: newHeader.Height, + NextValidatorsHash: newHeader.NextValidatorsHash, + Hash: newHeader.Hash(), + }) + if err != nil { + return emptyCtx, err + } + + newHeader.AppHash = resp.AppHash + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + AppHash: resp.AppHash, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + _, err = h.App.Commit() + if err != nil { + return emptyCtx, err + } + + return h.Ctx, nil +} + +func (h *Helper) ApplyEmptyBlockWithValSet(r *rand.Rand, valSetWithKeys *datagen.GenesisValidators) (sdk.Context, error) { + emptyCtx := sdk.Context{} + if h.App.LastBlockHeight() == 0 { + if err := h.genAndApplyEmptyBlock(); err != nil { + return emptyCtx, err + } + } + prevHeight := h.App.LastBlockHeight() + epoch := h.App.EpochingKeeper.GetEpoch(h.Ctx) + newHeight := prevHeight + 1 + + // 1. get previous vote extensions + prevEpoch := epoch.EpochNumber + blockHash := datagen.GenRandomBlockHash(r) + extendedVotes, err := h.getExtendedVotesFromValSet(prevEpoch, uint64(prevHeight), blockHash, valSetWithKeys) + if err != nil { + return emptyCtx, err + } + + // 2. create new header + valSet, err := h.App.StakingKeeper.GetLastValidators(h.Ctx) + if err != nil { + return emptyCtx, err + } + valhash := CalculateValHash(valSet) + newHeader := cmttypes.Header{ + Height: newHeight, + ValidatorsHash: valhash, + NextValidatorsHash: valhash, + LastBlockID: cmttypes.BlockID{ + Hash: datagen.GenRandomByteArray(r, 32), + }, + } + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + // 3. prepare proposal with previous BLS sigs + blockTxs := [][]byte{} + ppRes, err := h.App.PrepareProposal(&abci.RequestPrepareProposal{ + LocalLastCommit: abci.ExtendedCommitInfo{Votes: extendedVotes}, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + if len(ppRes.Txs) > 0 { + blockTxs = ppRes.Txs + } + _, err = h.App.ProcessProposal(&abci.RequestProcessProposal{ + Txs: ppRes.Txs, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + // 4. finalize block + resp, err := h.App.FinalizeBlock(&abci.RequestFinalizeBlock{ + Txs: blockTxs, + Height: newHeader.Height, + NextValidatorsHash: newHeader.NextValidatorsHash, + Hash: newHeader.Hash(), + }) + if err != nil { + return emptyCtx, err + } + + newHeader.AppHash = resp.AppHash + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + AppHash: resp.AppHash, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + _, err = h.App.Commit() + if err != nil { + return emptyCtx, err + } + + return h.Ctx, nil +} + +func (h *Helper) ApplyEmptyBlockWithInvalidBLSSig(r *rand.Rand) (sdk.Context, error) { + emptyCtx := sdk.Context{} + if h.App.LastBlockHeight() == 0 { + if err := h.genAndApplyEmptyBlock(); err != nil { + return emptyCtx, err + } + } + valSetWithKeys := h.GenValidators + prevHeight := h.App.LastBlockHeight() + epoch := h.App.EpochingKeeper.GetEpoch(h.Ctx) + newHeight := prevHeight + 1 + + // 1. get vote extensions with invalid BLS signature + prevEpoch := epoch.EpochNumber + blockHash := datagen.GenRandomBlockHash(r) + extendedVotes, err := datagen.GenRandomVoteExtension(prevEpoch, uint64(prevHeight), blockHash, valSetWithKeys, r) + if err != nil { + return emptyCtx, err + } + + res, err := h.App.VerifyVoteExtension(&abci.RequestVerifyVoteExtension{ + Hash: blockHash, + Height: prevHeight, + VoteExtension: extendedVotes[0].VoteExtension, + }) + if err != nil || !res.IsAccepted() { + return emptyCtx, fmt.Errorf("invalid vote extension") + } + + // 2. create new header + valSet, err := h.App.StakingKeeper.GetLastValidators(h.Ctx) + if err != nil { + return emptyCtx, err + } + valhash := CalculateValHash(valSet) + newHeader := cmttypes.Header{ + Height: newHeight, + ValidatorsHash: valhash, + NextValidatorsHash: valhash, + LastBlockID: cmttypes.BlockID{ + Hash: datagen.GenRandomByteArray(r, 32), + }, + } + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + // 3. prepare proposal with previous BLS sigs + blockTxs := [][]byte{} + ppRes, err := h.App.PrepareProposal(&abci.RequestPrepareProposal{ + LocalLastCommit: abci.ExtendedCommitInfo{Votes: extendedVotes}, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + if len(ppRes.Txs) > 0 { + blockTxs = ppRes.Txs + } + _, err = h.App.ProcessProposal(&abci.RequestProcessProposal{ + Txs: ppRes.Txs, + Height: newHeight, + }) + if err != nil { + return emptyCtx, err + } + + // 4. finalize block + resp, err := h.App.FinalizeBlock(&abci.RequestFinalizeBlock{ + Txs: blockTxs, + Height: newHeader.Height, + NextValidatorsHash: newHeader.NextValidatorsHash, + Hash: newHeader.Hash(), + }) + if err != nil { + return emptyCtx, err + } + + newHeader.AppHash = resp.AppHash + h.Ctx = h.Ctx.WithHeaderInfo(header.Info{ + Height: newHeader.Height, + AppHash: resp.AppHash, + Hash: newHeader.Hash(), + }).WithBlockHeader(*newHeader.ToProto()) + + _, err = h.App.Commit() + if err != nil { + return emptyCtx, err + } + + return h.Ctx, nil +} + +// CalculateValHash calculate validator hash and new header +// (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/simapp/test_helpers.go#L156-L163) +func CalculateValHash(valSet []stakingtypes.Validator) []byte { + bzs := make([][]byte, len(valSet)) + for i, val := range valSet { + consAddr, _ := val.GetConsAddr() + bzs[i] = consAddr + } + return merkle.HashFromByteSlices(bzs) +} diff --git a/testutil/helper/helper.go b/testutil/helper/helper.go new file mode 100644 index 000000000..a59093b5d --- /dev/null +++ b/testutil/helper/helper.go @@ -0,0 +1,245 @@ +package helper + +import ( + "bytes" + "testing" + + "cosmossdk.io/core/header" + abci "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/baseapp" + cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + protoio "github.com/cosmos/gogoproto/io" + + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/testutil/datagen" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + + "cosmossdk.io/math" + "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/babylonchain/babylon/app" + appparams "github.com/babylonchain/babylon/app/params" + btcstakingtypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/epoching/keeper" + "github.com/babylonchain/babylon/x/epoching/types" +) + +// Helper is a structure which wraps the entire app and exposes functionalities for testing the epoching module +type Helper struct { + t *testing.T + + Ctx sdk.Context + App *app.BabylonApp + MsgSrvr types.MsgServer + QueryClient types.QueryClient + + GenAccs []authtypes.GenesisAccount + GenValidators *datagen.GenesisValidators +} + +// NewHelper creates the helper for testing the epoching module +func NewHelper(t *testing.T) *Helper { + valSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(1) + require.NoError(t, err) + + return NewHelperWithValSet(t, valSet, privSigner) +} + +// NewHelperWithValSet is same as NewHelper, except that it creates a set of validators +// the privSigner is the 0th validator in valSet +func NewHelperWithValSet(t *testing.T, valSet *datagen.GenesisValidators, privSigner *app.PrivSigner) *Helper { + // generate the genesis account + signerPubKey := privSigner.WrappedPV.Key.PubKey + acc := authtypes.NewBaseAccount(signerPubKey.Address().Bytes(), &cosmosed.PubKey{Key: signerPubKey.Bytes()}, 0, 0) + privSigner.WrappedPV.Key.DelegatorAddress = acc.Address + valSet.Keys[0].ValidatorAddress = privSigner.WrappedPV.GetAddress().String() + // ensure the genesis account has a sufficient amount of tokens + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.DefaultPowerReduction.MulRaw(10000000))), + } + GenAccs := []authtypes.GenesisAccount{acc} + + // setup the app and ctx + app := app.SetupWithGenesisValSet(t, valSet.GetGenesisKeys(), privSigner, GenAccs, balance) + ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) // NOTE: height is 1 + + // get necessary subsets of the app/keeper + epochingKeeper := app.EpochingKeeper + querier := keeper.Querier{Keeper: epochingKeeper} + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, querier) + queryClient := types.NewQueryClient(queryHelper) + msgSrvr := keeper.NewMsgServerImpl(epochingKeeper) + + return &Helper{ + t, + ctx, + app, + msgSrvr, + queryClient, + GenAccs, + valSet, + } +} + +func (h *Helper) NoError(err error) { + require.NoError(h.t, err) +} + +func (h *Helper) getExtendedVotesFromValSet(epochNum, height uint64, blockHash checkpointingtypes.BlockHash, valSet *datagen.GenesisValidators) ([]abci.ExtendedVoteInfo, error) { + valPrivKey := valSet.GetValPrivKeys() + blsPrivKeys := valSet.GetBLSPrivKeys() + genesisKeys := valSet.GetGenesisKeys() + signBytes := checkpointingtypes.GetSignBytes(epochNum, blockHash) + extendedVotes := make([]abci.ExtendedVoteInfo, 0, len(valSet.Keys)) + for i, sk := range blsPrivKeys { + // 1. set build vote extension + sig := bls12381.Sign(sk, signBytes) + ve := checkpointingtypes.VoteExtension{ + Signer: genesisKeys[i].ValidatorAddress, + BlockHash: blockHash, + EpochNum: epochNum, + Height: height, + BlsSig: &sig, + } + veBytes, err := ve.Marshal() + if err != nil { + return nil, err + } + + // 2. sign validator signature over the vote extension + cve := cmtproto.CanonicalVoteExtension{ + Extension: veBytes, + Height: int64(height), + Round: int64(0), + ChainId: h.App.ChainID(), + } + var cveBuffer bytes.Buffer + err = protoio.NewDelimitedWriter(&cveBuffer).WriteMsg(&cve) + if err != nil { + return nil, err + } + cveBytes := cveBuffer.Bytes() + extensionSig, err := valPrivKey[i].Sign(cveBytes) + if err != nil { + return nil, err + } + + // 3. set up the validator of the vote extension + valAddress, err := sdk.ValAddressFromBech32(genesisKeys[i].ValidatorAddress) + if err != nil { + return nil, err + } + val := abci.Validator{ + Address: valAddress.Bytes(), + Power: 1000, + } + + // 4. construct the extended vote info + veInfo := abci.ExtendedVoteInfo{ + Validator: val, + VoteExtension: veBytes, + BlockIdFlag: cmtproto.BlockIDFlagCommit, + ExtensionSignature: extensionSig, + } + extendedVotes = append(extendedVotes, veInfo) + } + + return extendedVotes, nil +} + +// WrappedDelegate calls handler to delegate stake for a validator +func (h *Helper) WrappedDelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount math.Int) *sdk.Result { + coin := sdk.NewCoin(appparams.DefaultBondDenom, amount) + msg := stakingtypes.NewMsgDelegate(delegator.String(), val.String(), coin) + wmsg := types.NewMsgWrappedDelegate(msg) + return h.Handle(func(ctx sdk.Context) (proto.Message, error) { + return h.MsgSrvr.WrappedDelegate(ctx, wmsg) + }) +} + +// WrappedDelegateWithPower calls handler to delegate stake for a validator +func (h *Helper) WrappedDelegateWithPower(delegator sdk.AccAddress, val sdk.ValAddress, power int64) *sdk.Result { + coin := sdk.NewCoin(appparams.DefaultBondDenom, h.App.StakingKeeper.TokensFromConsensusPower(h.Ctx, power)) + msg := stakingtypes.NewMsgDelegate(delegator.String(), val.String(), coin) + wmsg := types.NewMsgWrappedDelegate(msg) + return h.Handle(func(ctx sdk.Context) (proto.Message, error) { + return h.MsgSrvr.WrappedDelegate(ctx, wmsg) + }) +} + +// WrappedUndelegate calls handler to unbound some stake from a validator. +func (h *Helper) WrappedUndelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount math.Int) *sdk.Result { + unbondAmt := sdk.NewCoin(appparams.DefaultBondDenom, amount) + msg := stakingtypes.NewMsgUndelegate(delegator.String(), val.String(), unbondAmt) + wmsg := types.NewMsgWrappedUndelegate(msg) + return h.Handle(func(ctx sdk.Context) (proto.Message, error) { + return h.MsgSrvr.WrappedUndelegate(ctx, wmsg) + }) +} + +// WrappedBeginRedelegate calls handler to redelegate some stake from a validator to another +func (h *Helper) WrappedBeginRedelegate(delegator sdk.AccAddress, srcVal sdk.ValAddress, dstVal sdk.ValAddress, amount math.Int) *sdk.Result { + unbondAmt := sdk.NewCoin(appparams.DefaultBondDenom, amount) + msg := stakingtypes.NewMsgBeginRedelegate(delegator.String(), srcVal.String(), dstVal.String(), unbondAmt) + wmsg := types.NewMsgWrappedBeginRedelegate(msg) + return h.Handle(func(ctx sdk.Context) (proto.Message, error) { + return h.MsgSrvr.WrappedBeginRedelegate(ctx, wmsg) + }) +} + +// WrappedCancelUnbondingDelegation calls handler to cancel unbonding a delegation +func (h *Helper) WrappedCancelUnbondingDelegation(delegator sdk.AccAddress, val sdk.ValAddress, amount math.Int, creationHeight int64) *sdk.Result { + unbondAmt := sdk.NewCoin(appparams.DefaultBondDenom, amount) + msg := stakingtypes.NewMsgCancelUnbondingDelegation(delegator.String(), val.String(), creationHeight, unbondAmt) + wmsg := types.NewMsgWrappedCancelUnbondingDelegation(msg) + return h.Handle(func(ctx sdk.Context) (proto.Message, error) { + return h.MsgSrvr.WrappedCancelUnbondingDelegation(ctx, wmsg) + }) +} + +// Handle executes an action function with the Helper's context, wraps the result into an SDK service result, and performs two assertions before returning it +func (h *Helper) Handle(action func(sdk.Context) (proto.Message, error)) *sdk.Result { + res, err := action(h.Ctx) + require.NoError(h.t, err) + r, err := sdk.WrapServiceResult(h.Ctx, res, err) + require.NoError(h.t, err) + require.NotNil(h.t, r) + require.NoError(h.t, err) + return r +} + +// CheckValidator asserts that a validor exists and has a given status (if status!="") +// and if has a right jailed flag. +func (h *Helper) CheckValidator(addr sdk.ValAddress, status stakingtypes.BondStatus, jailed bool) stakingtypes.Validator { + v, err := h.App.StakingKeeper.GetValidator(h.Ctx, addr) + require.NoError(h.t, err) + require.Equal(h.t, jailed, v.Jailed, "wrong Jalied status") + if status >= 0 { + require.Equal(h.t, status, v.Status) + } + return v +} + +// CheckDelegator asserts that a delegator exists +func (h *Helper) CheckDelegator(delegator sdk.AccAddress, val sdk.ValAddress, found bool) { + _, ok := h.App.StakingKeeper.GetDelegation(h.Ctx, delegator, val) + require.Equal(h.t, ok, found) +} + +func (h *Helper) AddDelegation(del *btcstakingtypes.BTCDelegation) { + err := h.App.BTCStakingKeeper.AddBTCDelegation(h.Ctx, del) + h.NoError(err) +} + +func (h *Helper) AddFinalityProvider(fp *btcstakingtypes.FinalityProvider) { + h.App.BTCStakingKeeper.SetFinalityProvider(h.Ctx, fp) +} diff --git a/testutil/keeper/btccheckpoint.go b/testutil/keeper/btccheckpoint.go index ecd1f7c4f..528c059f5 100644 --- a/testutil/keeper/btccheckpoint.go +++ b/testutil/keeper/btccheckpoint.go @@ -1,38 +1,40 @@ package keeper import ( - "testing" - "math/big" + "testing" - "github.com/babylonchain/babylon/x/btccheckpoint/keeper" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - tmdb "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/btccheckpoint/keeper" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" ) func NewBTCCheckpointKeeper( t testing.TB, lk btcctypes.BTCLightClientKeeper, ek btcctypes.CheckpointingKeeper, + ik btcctypes.IncentiveKeeper, powLimit *big.Int) (*keeper.Keeper, sdk.Context) { - storeKey := sdk.NewKVStoreKey(btcctypes.StoreKey) - tstoreKey := sdk.NewTransientStoreKey(btcctypes.TStoreKey) - memStoreKey := storetypes.NewMemoryStoreKey(btcctypes.MemStoreKey) + storeKey := storetypes.NewKVStoreKey(btcctypes.StoreKey) + tstoreKey := storetypes.NewTransientStoreKey(btcctypes.TStoreKey) - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) - stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) require.NoError(t, stateStore.LoadLatestVersion()) registry := codectypes.NewInterfaceRegistry() @@ -40,16 +42,17 @@ func NewBTCCheckpointKeeper( k := keeper.NewKeeper( cdc, - storeKey, + runtime.NewKVStoreService(storeKey), tstoreKey, - memStoreKey, lk, ek, + ik, powLimit, authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) // Initialize params if err := k.SetParams(ctx, btcctypes.DefaultParams()); err != nil { diff --git a/testutil/keeper/btclightclient.go b/testutil/keeper/btclightclient.go index f6b887b3f..d4e371894 100644 --- a/testutil/keeper/btclightclient.go +++ b/testutil/keeper/btclightclient.go @@ -3,29 +3,37 @@ package keeper import ( "testing" - bapp "github.com/babylonchain/babylon/app" - bbn "github.com/babylonchain/babylon/types" - "github.com/babylonchain/babylon/x/btclightclient/keeper" - "github.com/babylonchain/babylon/x/btclightclient/types" - tmdb "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/stretchr/testify/require" + + bapp "github.com/babylonchain/babylon/app" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btclightclient/keeper" + "github.com/babylonchain/babylon/x/btclightclient/types" ) func BTCLightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { - storeKey := sdk.NewKVStoreKey(types.StoreKey) - memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + return BTCLightClientKeeperWithCustomParams(t, types.DefaultParams()) +} + +func BTCLightClientKeeperWithCustomParams(t testing.TB, p types.Params) (*keeper.Keeper, sdk.Context) { + storeKey := storetypes.NewKVStoreKey(types.StoreKey) - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) - stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) require.NoError(t, stateStore.LoadLatestVersion()) registry := codectypes.NewInterfaceRegistry() @@ -35,12 +43,17 @@ func BTCLightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { k := keeper.NewKeeper( cdc, - storeKey, - memStoreKey, + runtime.NewKVStoreService(storeKey), testCfg, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) + + if err := k.SetParams(ctx, p); err != nil { + panic(err) + } return &k, ctx } diff --git a/testutil/keeper/btcstaking.go b/testutil/keeper/btcstaking.go new file mode 100644 index 000000000..58f2af369 --- /dev/null +++ b/testutil/keeper/btcstaking.go @@ -0,0 +1,59 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + "github.com/btcsuite/btcd/chaincfg" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/btcstaking/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +func BTCStakingKeeper( + t testing.TB, + btclcKeeper types.BTCLightClientKeeper, + btccKeeper types.BtcCheckpointKeeper, +) (*keeper.Keeper, sdk.Context) { + storeKey := storetypes.NewKVStoreKey(types.StoreKey) + + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + + k := keeper.NewKeeper( + cdc, + runtime.NewKVStoreService(storeKey), + btclcKeeper, + btccKeeper, + &chaincfg.SimNetParams, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) + + // Initialize params + if err := k.SetParams(ctx, types.DefaultParams()); err != nil { + panic(err) + } + + return &k, ctx +} diff --git a/testutil/keeper/checkpointing.go b/testutil/keeper/checkpointing.go index 895ae3ead..9014c328d 100644 --- a/testutil/keeper/checkpointing.go +++ b/testutil/keeper/checkpointing.go @@ -3,28 +3,29 @@ package keeper import ( "testing" - "github.com/babylonchain/babylon/x/checkpointing/keeper" - "github.com/babylonchain/babylon/x/checkpointing/types" - tmdb "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/client" + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/checkpointing/keeper" + "github.com/babylonchain/babylon/x/checkpointing/types" ) -func CheckpointingKeeper(t testing.TB, ek types.EpochingKeeper, signer keeper.BlsSigner, cliCtx client.Context) (*keeper.Keeper, sdk.Context, *codec.ProtoCodec) { - storeKey := sdk.NewKVStoreKey(types.StoreKey) - memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) +func CheckpointingKeeper(t testing.TB, ek types.EpochingKeeper, signer keeper.BlsSigner) (*keeper.Keeper, sdk.Context, *codec.ProtoCodec) { + storeKey := storetypes.NewKVStoreKey(types.StoreKey) - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) - stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) require.NoError(t, stateStore.LoadLatestVersion()) registry := codectypes.NewInterfaceRegistry() @@ -33,14 +34,13 @@ func CheckpointingKeeper(t testing.TB, ek types.EpochingKeeper, signer keeper.Bl k := keeper.NewKeeper( cdc, - storeKey, - memStoreKey, + runtime.NewKVStoreService(storeKey), signer, ek, - cliCtx, ) - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) return &k, ctx, cdc } diff --git a/testutil/keeper/epoching.go b/testutil/keeper/epoching.go index d1dab7cdc..902a11593 100644 --- a/testutil/keeper/epoching.go +++ b/testutil/keeper/epoching.go @@ -3,13 +3,16 @@ package keeper import ( "testing" - tmdb "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -20,13 +23,11 @@ import ( ) func EpochingKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { - storeKey := sdk.NewKVStoreKey(types.StoreKey) - memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + storeKey := storetypes.NewKVStoreKey(types.StoreKey) - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) - stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) require.NoError(t, stateStore.LoadLatestVersion()) registry := codectypes.NewInterfaceRegistry() @@ -34,8 +35,7 @@ func EpochingKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { k := keeper.NewKeeper( cdc, - storeKey, - memStoreKey, + runtime.NewKVStoreService(storeKey), // TODO: make this compile at the moment, will fix for integrated testing nil, nil, @@ -44,7 +44,8 @@ func EpochingKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { // TODO: add msgServiceRouter? - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) // Initialize params if err := k.SetParams(ctx, types.DefaultParams()); err != nil { diff --git a/testutil/keeper/finality.go b/testutil/keeper/finality.go new file mode 100644 index 000000000..83b6ce264 --- /dev/null +++ b/testutil/keeper/finality.go @@ -0,0 +1,53 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" +) + +func FinalityKeeper(t testing.TB, bsKeeper types.BTCStakingKeeper, iKeeper types.IncentiveKeeper) (*keeper.Keeper, sdk.Context) { + storeKey := storetypes.NewKVStoreKey(types.StoreKey) + + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + + k := keeper.NewKeeper( + cdc, + runtime.NewKVStoreService(storeKey), + bsKeeper, + iKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) + + // Initialize params + if err := k.SetParams(ctx, types.DefaultParams()); err != nil { + panic(err) + } + + return &k, ctx +} diff --git a/testutil/keeper/incentive.go b/testutil/keeper/incentive.go new file mode 100644 index 000000000..f5a23be90 --- /dev/null +++ b/testutil/keeper/incentive.go @@ -0,0 +1,55 @@ +package keeper + +import ( + "testing" + + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/incentive/keeper" + "github.com/babylonchain/babylon/x/incentive/types" +) + +func IncentiveKeeper(t testing.TB, bankKeeper types.BankKeeper, accountKeeper types.AccountKeeper, epochingKeeper types.EpochingKeeper) (*keeper.Keeper, sdk.Context) { + storeKey := storetypes.NewKVStoreKey(types.StoreKey) + + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + + k := keeper.NewKeeper( + cdc, + runtime.NewKVStoreService(storeKey), + bankKeeper, + accountKeeper, + epochingKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + authtypes.FeeCollectorName, + ) + + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) + ctx = ctx.WithHeaderInfo(header.Info{}) + + // Initialize params + if err := k.SetParams(ctx, types.DefaultParams()); err != nil { + panic(err) + } + + return &k, ctx +} diff --git a/testutil/keeper/zoneconcierge.go b/testutil/keeper/zoneconcierge.go index 5d11eff1d..1c5478db4 100644 --- a/testutil/keeper/zoneconcierge.go +++ b/testutil/keeper/zoneconcierge.go @@ -3,25 +3,28 @@ package keeper import ( "testing" - "github.com/babylonchain/babylon/x/zoneconcierge/keeper" - "github.com/babylonchain/babylon/x/zoneconcierge/types" - tmdb "github.com/cometbft/cometbft-db" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/log" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/core/header" + "cosmossdk.io/log" + "cosmossdk.io/store" + "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/runtime" 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" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/zoneconcierge/keeper" + "github.com/babylonchain/babylon/x/zoneconcierge/types" ) // zoneconciergeChannelKeeper is a stub of ChannelKeeper @@ -56,24 +59,23 @@ func (zoneconciergePortKeeper) BindPort(ctx sdk.Context, portID string) *capabil type zoneconciergeStoreQuerier struct{} -func (zoneconciergeStoreQuerier) Query(req abci.RequestQuery) abci.ResponseQuery { - return abci.ResponseQuery{ - ProofOps: &tmcrypto.ProofOps{ - Ops: []tmcrypto.ProofOp{ - tmcrypto.ProofOp{}, +func (zoneconciergeStoreQuerier) Query(req *storetypes.RequestQuery) (*storetypes.ResponseQuery, error) { + return &storetypes.ResponseQuery{ + ProofOps: &cmtcrypto.ProofOps{ + Ops: []cmtcrypto.ProofOp{ + cmtcrypto.ProofOp{}, }, }, - } + }, nil } -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) +func ZoneConciergeKeeper(t testing.TB, btclcKeeper types.BTCLightClientKeeper, checkpointingKeeper types.CheckpointingKeeper, btccKeeper types.BtcCheckpointKeeper, epochingKeeper types.EpochingKeeper) (*keeper.Keeper, sdk.Context) { + logger := log.NewTestLogger(t) + storeKey := storetypes.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) + db := dbm.NewMemDB() + stateStore := store.NewCommitMultiStore(db, logger, metrics.NewNoOpMetrics()) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) require.NoError(t, stateStore.LoadLatestVersion()) @@ -83,8 +85,8 @@ func ZoneConciergeKeeper(t testing.TB, btclcKeeper types.BTCLightClientKeeper, c capabilityKeeper := capabilitykeeper.NewKeeper(appCodec, storeKey, memStoreKey) k := keeper.NewKeeper( appCodec, - storeKey, - memStoreKey, + runtime.NewKVStoreService(storeKey), + nil, // TODO: mock this keeper nil, // TODO: mock this keeper zoneconciergeChannelKeeper{}, zoneconciergePortKeeper{}, @@ -94,13 +96,13 @@ func ZoneConciergeKeeper(t testing.TB, btclcKeeper types.BTCLightClientKeeper, c checkpointingKeeper, btccKeeper, epochingKeeper, - tmClient, zoneconciergeStoreQuerier{}, capabilityKeeper.ScopeToModule("ZoneconciergeScopedKeeper"), authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, logger) + ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, logger) + ctx = ctx.WithHeaderInfo(header.Info{}) return k, ctx } diff --git a/testutil/mocks/bls_signer.go b/testutil/mocks/bls_signer.go index d846b7d20..094e5b89c 100644 --- a/testutil/mocks/bls_signer.go +++ b/testutil/mocks/bls_signer.go @@ -8,6 +8,7 @@ import ( reflect "reflect" bls12381 "github.com/babylonchain/babylon/crypto/bls12381" + crypto "github.com/cometbft/cometbft/crypto" types "github.com/cosmos/cosmos-sdk/types" gomock "github.com/golang/mock/gomock" ) @@ -64,6 +65,21 @@ func (mr *MockBlsSignerMockRecorder) GetBlsPubkey() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlsPubkey", reflect.TypeOf((*MockBlsSigner)(nil).GetBlsPubkey)) } +// GetValidatorPubkey mocks base method. +func (m *MockBlsSigner) GetValidatorPubkey() (crypto.PubKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatorPubkey") + ret0, _ := ret[0].(crypto.PubKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidatorPubkey indicates an expected call of GetValidatorPubkey. +func (mr *MockBlsSignerMockRecorder) GetValidatorPubkey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorPubkey", reflect.TypeOf((*MockBlsSigner)(nil).GetValidatorPubkey)) +} + // SignMsgWithBls mocks base method. func (m *MockBlsSigner) SignMsgWithBls(msg []byte) (bls12381.Signature, error) { m.ctrl.T.Helper() diff --git a/testutil/mocks/checkpointing_expected_keepers.go b/testutil/mocks/checkpointing_expected_keepers.go index 2c7327500..409a1071b 100644 --- a/testutil/mocks/checkpointing_expected_keepers.go +++ b/testutil/mocks/checkpointing_expected_keepers.go @@ -5,90 +5,17 @@ package mocks import ( + context "context" reflect "reflect" types "github.com/babylonchain/babylon/x/checkpointing/types" types0 "github.com/babylonchain/babylon/x/epoching/types" + crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" types1 "github.com/cosmos/cosmos-sdk/types" - types2 "github.com/cosmos/cosmos-sdk/x/auth/types" - types3 "github.com/cosmos/cosmos-sdk/x/staking/types" + types2 "github.com/cosmos/cosmos-sdk/x/staking/types" gomock "github.com/golang/mock/gomock" ) -// MockAccountKeeper is a mock of AccountKeeper interface. -type MockAccountKeeper struct { - ctrl *gomock.Controller - recorder *MockAccountKeeperMockRecorder -} - -// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. -type MockAccountKeeperMockRecorder struct { - mock *MockAccountKeeper -} - -// NewMockAccountKeeper creates a new mock instance. -func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { - mock := &MockAccountKeeper{ctrl: ctrl} - mock.recorder = &MockAccountKeeperMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { - return m.recorder -} - -// GetAccount mocks base method. -func (m *MockAccountKeeper) GetAccount(ctx types1.Context, addr types1.AccAddress) types2.AccountI { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccount", ctx, addr) - ret0, _ := ret[0].(types2.AccountI) - return ret0 -} - -// GetAccount indicates an expected call of GetAccount. -func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) -} - -// MockBankKeeper is a mock of BankKeeper interface. -type MockBankKeeper struct { - ctrl *gomock.Controller - recorder *MockBankKeeperMockRecorder -} - -// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. -type MockBankKeeperMockRecorder struct { - mock *MockBankKeeper -} - -// NewMockBankKeeper creates a new mock instance. -func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { - mock := &MockBankKeeper{ctrl: ctrl} - mock.recorder = &MockBankKeeperMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { - return m.recorder -} - -// SpendableCoins mocks base method. -func (m *MockBankKeeper) SpendableCoins(ctx types1.Context, addr types1.AccAddress) types1.Coins { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr) - ret0, _ := ret[0].(types1.Coins) - return ret0 -} - -// SpendableCoins indicates an expected call of SpendableCoins. -func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) -} - // MockEpochingKeeper is a mock of EpochingKeeper interface. type MockEpochingKeeper struct { ctrl *gomock.Controller @@ -113,7 +40,7 @@ func (m *MockEpochingKeeper) EXPECT() *MockEpochingKeeperMockRecorder { } // CheckMsgCreateValidator mocks base method. -func (m *MockEpochingKeeper) CheckMsgCreateValidator(ctx types1.Context, msg *types3.MsgCreateValidator) error { +func (m *MockEpochingKeeper) CheckMsgCreateValidator(ctx context.Context, msg *types2.MsgCreateValidator) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckMsgCreateValidator", ctx, msg) ret0, _ := ret[0].(error) @@ -127,7 +54,7 @@ func (mr *MockEpochingKeeperMockRecorder) CheckMsgCreateValidator(ctx, msg inter } // EnqueueMsg mocks base method. -func (m *MockEpochingKeeper) EnqueueMsg(ctx types1.Context, msg types0.QueuedMessage) { +func (m *MockEpochingKeeper) EnqueueMsg(ctx context.Context, msg types0.QueuedMessage) { m.ctrl.T.Helper() m.ctrl.Call(m, "EnqueueMsg", ctx, msg) } @@ -138,8 +65,23 @@ func (mr *MockEpochingKeeperMockRecorder) EnqueueMsg(ctx, msg interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnqueueMsg", reflect.TypeOf((*MockEpochingKeeper)(nil).EnqueueMsg), ctx, msg) } +// GetAppHash mocks base method. +func (m *MockEpochingKeeper) GetAppHash(ctx context.Context, height uint64) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAppHash", ctx, height) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppHash indicates an expected call of GetAppHash. +func (mr *MockEpochingKeeperMockRecorder) GetAppHash(ctx, height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppHash", reflect.TypeOf((*MockEpochingKeeper)(nil).GetAppHash), ctx, height) +} + // GetEpoch mocks base method. -func (m *MockEpochingKeeper) GetEpoch(ctx types1.Context) *types0.Epoch { +func (m *MockEpochingKeeper) GetEpoch(ctx context.Context) *types0.Epoch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEpoch", ctx) ret0, _ := ret[0].(*types0.Epoch) @@ -152,8 +94,23 @@ func (mr *MockEpochingKeeperMockRecorder) GetEpoch(ctx interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpoch", reflect.TypeOf((*MockEpochingKeeper)(nil).GetEpoch), ctx) } +// GetPubKeyByConsAddr mocks base method. +func (m *MockEpochingKeeper) GetPubKeyByConsAddr(ctx context.Context, consAddr types1.ConsAddress) (crypto.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPubKeyByConsAddr", ctx, consAddr) + ret0, _ := ret[0].(crypto.PublicKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPubKeyByConsAddr indicates an expected call of GetPubKeyByConsAddr. +func (mr *MockEpochingKeeperMockRecorder) GetPubKeyByConsAddr(ctx, consAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPubKeyByConsAddr", reflect.TypeOf((*MockEpochingKeeper)(nil).GetPubKeyByConsAddr), ctx, consAddr) +} + // GetTotalVotingPower mocks base method. -func (m *MockEpochingKeeper) GetTotalVotingPower(ctx types1.Context, epochNumber uint64) int64 { +func (m *MockEpochingKeeper) GetTotalVotingPower(ctx context.Context, epochNumber uint64) int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTotalVotingPower", ctx, epochNumber) ret0, _ := ret[0].(int64) @@ -167,7 +124,7 @@ func (mr *MockEpochingKeeperMockRecorder) GetTotalVotingPower(ctx, epochNumber i } // GetValidatorSet mocks base method. -func (m *MockEpochingKeeper) GetValidatorSet(ctx types1.Context, epochNumer uint64) types0.ValidatorSet { +func (m *MockEpochingKeeper) GetValidatorSet(ctx context.Context, epochNumer uint64) types0.ValidatorSet { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorSet", ctx, epochNumer) ret0, _ := ret[0].(types0.ValidatorSet) @@ -204,7 +161,7 @@ func (m *MockCheckpointingHooks) EXPECT() *MockCheckpointingHooksMockRecorder { } // AfterBlsKeyRegistered mocks base method. -func (m *MockCheckpointingHooks) AfterBlsKeyRegistered(ctx types1.Context, valAddr types1.ValAddress) error { +func (m *MockCheckpointingHooks) AfterBlsKeyRegistered(ctx context.Context, valAddr types1.ValAddress) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AfterBlsKeyRegistered", ctx, valAddr) ret0, _ := ret[0].(error) @@ -218,7 +175,7 @@ func (mr *MockCheckpointingHooksMockRecorder) AfterBlsKeyRegistered(ctx, valAddr } // AfterRawCheckpointBlsSigVerified mocks base method. -func (m *MockCheckpointingHooks) AfterRawCheckpointBlsSigVerified(ctx types1.Context, ckpt *types.RawCheckpoint) error { +func (m *MockCheckpointingHooks) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *types.RawCheckpoint) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AfterRawCheckpointBlsSigVerified", ctx, ckpt) ret0, _ := ret[0].(error) @@ -232,7 +189,7 @@ func (mr *MockCheckpointingHooksMockRecorder) AfterRawCheckpointBlsSigVerified(c } // AfterRawCheckpointConfirmed mocks base method. -func (m *MockCheckpointingHooks) AfterRawCheckpointConfirmed(ctx types1.Context, epoch uint64) error { +func (m *MockCheckpointingHooks) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AfterRawCheckpointConfirmed", ctx, epoch) ret0, _ := ret[0].(error) @@ -246,7 +203,7 @@ func (mr *MockCheckpointingHooksMockRecorder) AfterRawCheckpointConfirmed(ctx, e } // AfterRawCheckpointFinalized mocks base method. -func (m *MockCheckpointingHooks) AfterRawCheckpointFinalized(ctx types1.Context, epoch uint64) error { +func (m *MockCheckpointingHooks) AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AfterRawCheckpointFinalized", ctx, epoch) ret0, _ := ret[0].(error) @@ -260,7 +217,7 @@ func (mr *MockCheckpointingHooksMockRecorder) AfterRawCheckpointFinalized(ctx, e } // AfterRawCheckpointForgotten mocks base method. -func (m *MockCheckpointingHooks) AfterRawCheckpointForgotten(ctx types1.Context, ckpt *types.RawCheckpoint) error { +func (m *MockCheckpointingHooks) AfterRawCheckpointForgotten(ctx context.Context, ckpt *types.RawCheckpoint) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AfterRawCheckpointForgotten", ctx, ckpt) ret0, _ := ret[0].(error) diff --git a/types/btc_config.go b/types/btc_config.go index ad24a4014..078b2a120 100644 --- a/types/btc_config.go +++ b/types/btc_config.go @@ -11,9 +11,7 @@ import ( type SupportedBtcNetwork string type BtcConfig struct { - powLimit *big.Int - retargetAdjustmentFactor int64 - reduceMinDifficulty bool + btcNetParams *chaincfg.Params } const ( @@ -21,9 +19,10 @@ const ( BtcTestnet SupportedBtcNetwork = "testnet" BtcSimnet SupportedBtcNetwork = "simnet" BtcRegtest SupportedBtcNetwork = "regtest" + BtcSignet SupportedBtcNetwork = "signet" ) -func getParams(opts servertypes.AppOptions) chaincfg.Params { +func getParams(opts servertypes.AppOptions) *chaincfg.Params { valueInterface := opts.Get("btc-config.network") if valueInterface == nil { @@ -37,49 +36,38 @@ func getParams(opts servertypes.AppOptions) chaincfg.Params { } if network == string(BtcMainnet) { - return chaincfg.MainNetParams + return &chaincfg.MainNetParams } else if network == string(BtcTestnet) { - return chaincfg.TestNet3Params + return &chaincfg.TestNet3Params } else if network == string(BtcSimnet) { - return chaincfg.SimNetParams + return &chaincfg.SimNetParams } else if network == string(BtcRegtest) { - return chaincfg.RegressionNetParams + return &chaincfg.RegressionNetParams + } else if network == string(BtcSignet) { + return &chaincfg.SigNetParams } else { - panic("Bitcoin network should be one of [mainet, testnet, simnet, regtest]") + panic("Bitcoin network should be one of [mainet, testnet, simnet, regtest, signet]") } } -func parsePowLimit(opts servertypes.AppOptions) *big.Int { - return getParams(opts).PowLimit -} - -func parseRetargetAdjustmentFactor(opts servertypes.AppOptions) int64 { - return getParams(opts).RetargetAdjustmentFactor -} - -func parseReduceMinDifficulty(opts servertypes.AppOptions) bool { - return getParams(opts).ReduceMinDifficulty -} - func ParseBtcOptionsFromConfig(opts servertypes.AppOptions) BtcConfig { - powLimit := parsePowLimit(opts) - retargetAdjustmentFactor := parseRetargetAdjustmentFactor(opts) - reduceMinDifficulty := parseReduceMinDifficulty(opts) return BtcConfig{ - powLimit: powLimit, - retargetAdjustmentFactor: retargetAdjustmentFactor, - reduceMinDifficulty: reduceMinDifficulty, + btcNetParams: getParams(opts), } } +func (c *BtcConfig) NetParams() *chaincfg.Params { + return c.btcNetParams +} + func (c *BtcConfig) PowLimit() big.Int { - return *c.powLimit + return *c.btcNetParams.PowLimit } func (c *BtcConfig) RetargetAdjustmentFactor() int64 { - return c.retargetAdjustmentFactor + return c.btcNetParams.RetargetAdjustmentFactor } func (c *BtcConfig) ReduceMinDifficulty() bool { - return c.reduceMinDifficulty + return c.btcNetParams.ReduceMinDifficulty } diff --git a/types/btc_schnorr_eots.go b/types/btc_schnorr_eots.go new file mode 100644 index 000000000..f8980db5b --- /dev/null +++ b/types/btc_schnorr_eots.go @@ -0,0 +1,81 @@ +package types + +import ( + "bytes" + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" +) + +type SchnorrEOTSSig []byte + +const SchnorrEOTSSigLen = 32 + +func NewSchnorrEOTSSig(data []byte) (*SchnorrEOTSSig, error) { + var sig SchnorrEOTSSig + err := sig.Unmarshal(data) + return &sig, err +} + +func NewSchnorrEOTSSigFromHex(sigHex string) (*SchnorrEOTSSig, error) { + sigBytes, err := hex.DecodeString(sigHex) + if err != nil { + return nil, err + } + return NewSchnorrEOTSSig(sigBytes) +} + +func NewSchnorrEOTSSigFromModNScalar(s *btcec.ModNScalar) *SchnorrEOTSSig { + prBytes := s.Bytes() + sig := SchnorrEOTSSig(prBytes[:]) + return &sig +} + +func (sig SchnorrEOTSSig) ToModNScalar() *btcec.ModNScalar { + var s btcec.ModNScalar + s.SetByteSlice(sig) + return &s +} + +func (sig SchnorrEOTSSig) Size() int { + return len(sig.MustMarshal()) +} + +func (sig SchnorrEOTSSig) Marshal() ([]byte, error) { + return sig, nil +} + +func (sig SchnorrEOTSSig) MustMarshal() []byte { + prBytes, err := sig.Marshal() + if err != nil { + panic(err) + } + return prBytes +} + +func (sig SchnorrEOTSSig) MarshalTo(data []byte) (int, error) { + bz, err := sig.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +func (sig *SchnorrEOTSSig) Unmarshal(data []byte) error { + if len(data) != SchnorrEOTSSigLen { + return fmt.Errorf("invalid data length") + } + *sig = data + return nil +} + +func (sig *SchnorrEOTSSig) Equals(sig2 *SchnorrEOTSSig) bool { + return bytes.Equal(sig.MustMarshal(), sig2.MustMarshal()) +} + +func (sig *SchnorrEOTSSig) ToHexStr() string { + sigBytes := sig.MustMarshal() + return hex.EncodeToString(sigBytes) +} diff --git a/types/btc_schnorr_eots_test.go b/types/btc_schnorr_eots_test.go new file mode 100644 index 000000000..d6fe38575 --- /dev/null +++ b/types/btc_schnorr_eots_test.go @@ -0,0 +1,36 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/stretchr/testify/require" +) + +func FuzzSchnorrEOTSSig(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + randBytes := datagen.GenRandomByteArray(r, 32) + var modNScalar btcec.ModNScalar + overflowed := modNScalar.SetByteSlice(randBytes) + require.False(t, overflowed) + + // ModNScalar -> SchnorrEOTSSig -> ModNScalar + sig := types.NewSchnorrEOTSSigFromModNScalar(&modNScalar) + modNScalar2 := sig.ToModNScalar() + require.True(t, modNScalar.Equals(modNScalar2)) + + // SchnorrEOTSSig -> bytes -> SchnorrEOTSSig + randBytes2 := sig.MustMarshal() + sig2, err := types.NewSchnorrEOTSSig(randBytes) + require.NoError(t, err) + require.Equal(t, randBytes, randBytes2) + require.Equal(t, sig, sig2) + }) +} diff --git a/types/btc_schnorr_pk.go b/types/btc_schnorr_pk.go new file mode 100644 index 000000000..f297747dd --- /dev/null +++ b/types/btc_schnorr_pk.go @@ -0,0 +1,144 @@ +package types + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "sort" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" +) + +type BIP340PubKey []byte + +const BIP340PubKeyLen = schnorr.PubKeyBytesLen + +func NewBIP340PubKey(data []byte) (*BIP340PubKey, error) { + var pk BIP340PubKey + err := pk.Unmarshal(data) + return &pk, err +} + +func NewBIP340PubKeyFromHex(hexStr string) (*BIP340PubKey, error) { + var pk BIP340PubKey + err := pk.UnmarshalHex(hexStr) + return &pk, err +} + +func NewBIP340PubKeyFromBTCPK(btcPK *btcec.PublicKey) *BIP340PubKey { + pkBytes := schnorr.SerializePubKey(btcPK) + pk := BIP340PubKey(pkBytes) + return &pk +} + +func (pk BIP340PubKey) ToBTCPK() (*btcec.PublicKey, error) { + return schnorr.ParsePubKey(pk) +} + +func (pk BIP340PubKey) MustToBTCPK() *btcec.PublicKey { + btcPK, err := schnorr.ParsePubKey(pk) + if err != nil { + panic(err) + } + return btcPK +} + +func (pk *BIP340PubKey) MarshalHex() string { + return hex.EncodeToString(pk.MustMarshal()) +} + +func (pk *BIP340PubKey) UnmarshalHex(header string) error { + // Decode the hash string from hex + decoded, err := hex.DecodeString(header) + if err != nil { + return err + } + + return pk.Unmarshal(decoded) +} + +func (pk BIP340PubKey) Size() int { + return len(pk.MustMarshal()) +} + +func (pk BIP340PubKey) Marshal() ([]byte, error) { + return pk, nil +} + +func (pk BIP340PubKey) MustMarshal() []byte { + pkBytes, err := pk.Marshal() + if err != nil { + panic(err) + } + return pkBytes +} + +func (pk BIP340PubKey) MarshalTo(data []byte) (int, error) { + bz, err := pk.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +func (pk *BIP340PubKey) Unmarshal(data []byte) error { + if len(data) != BIP340PubKeyLen { + return fmt.Errorf("malformed data for BIP340 public key") + } + + *pk = data + return nil +} + +func (pk BIP340PubKey) MarshalJSON() ([]byte, error) { + return json.Marshal(pk.MarshalHex()) +} + +func (pk *BIP340PubKey) UnmarshalJSON(bz []byte) error { + var pkHexString string + err := json.Unmarshal(bz, &pkHexString) + + if err != nil { + return err + } + + return pk.UnmarshalHex(pkHexString) +} + +func (pk *BIP340PubKey) Equals(pk2 *BIP340PubKey) bool { + return bytes.Equal(*pk, *pk2) +} + +func NewBTCPKsFromBIP340PKs(pks []BIP340PubKey) ([]*btcec.PublicKey, error) { + btcPks := make([]*btcec.PublicKey, 0, len(pks)) + for _, pk := range pks { + btcPK, err := pk.ToBTCPK() + if err != nil { + return nil, err + } + btcPks = append(btcPks, btcPK) + } + return btcPks, nil +} + +func NewBIP340PKsFromBTCPKs(btcPKs []*btcec.PublicKey) []BIP340PubKey { + pks := make([]BIP340PubKey, 0, len(btcPKs)) + for _, btcPK := range btcPKs { + pks = append(pks, *NewBIP340PubKeyFromBTCPK(btcPK)) + } + return pks +} + +func SortBIP340PKs(keys []BIP340PubKey) []BIP340PubKey { + sortedPKs := make([]BIP340PubKey, len(keys)) + copy(sortedPKs, keys) + sort.SliceStable(sortedPKs, func(i, j int) bool { + keyIBytes := sortedPKs[i].MustMarshal() + keyJBytes := sortedPKs[j].MustMarshal() + return bytes.Compare(keyIBytes, keyJBytes) == 1 + }) + return sortedPKs +} diff --git a/types/btc_schnorr_pk_test.go b/types/btc_schnorr_pk_test.go new file mode 100644 index 000000000..6daf60629 --- /dev/null +++ b/types/btc_schnorr_pk_test.go @@ -0,0 +1,40 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/types" + "github.com/stretchr/testify/require" +) + +func FuzzBIP340PubKey(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + _, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + // btcPK -> BIP340PubKey -> btcPK + pk := types.NewBIP340PubKeyFromBTCPK(btcPK) + btcPK2, err := pk.ToBTCPK() + require.NoError(t, err) + // NOTE: we can only ensure they have the same x value. + // there could be 2 different y values for a given x on secp256k1 + // curve. The BIP340 encoding is compressed in the sense that + // it only contains x value but not y. pk.ToBTCPK() may choose a random + // one of the 2 possible y values. + require.Zero(t, btcPK.X().Cmp(btcPK2.X())) + + // pk -> bytes -> pk + pkBytes := pk.MustMarshal() + var pk2 types.BIP340PubKey + _, err = types.NewBIP340PubKey(pkBytes) + require.NoError(t, err) + err = pk2.Unmarshal(pkBytes) + require.NoError(t, err) + require.Equal(t, *pk, pk2) + }) +} diff --git a/types/btc_schnorr_pub_rand.go b/types/btc_schnorr_pub_rand.go new file mode 100644 index 000000000..be92231d2 --- /dev/null +++ b/types/btc_schnorr_pub_rand.go @@ -0,0 +1,76 @@ +package types + +import ( + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" +) + +type SchnorrPubRand []byte + +const SchnorrPubRandLen = 32 + +func NewSchnorrPubRand(data []byte) (*SchnorrPubRand, error) { + var pr SchnorrPubRand + err := pr.Unmarshal(data) + return &pr, err +} + +func NewSchnorrPubRandFromHex(prHex string) (*SchnorrPubRand, error) { + prBytes, err := hex.DecodeString(prHex) + if err != nil { + return nil, err + } + return NewSchnorrPubRand(prBytes) +} + +func NewSchnorrPubRandFromFieldVal(r *btcec.FieldVal) *SchnorrPubRand { + prBytes := r.Bytes() + pr := SchnorrPubRand(prBytes[:]) + return &pr +} + +func (pr SchnorrPubRand) ToFieldVal() *btcec.FieldVal { + var r btcec.FieldVal + r.SetByteSlice(pr) + return &r +} + +func (pr SchnorrPubRand) Size() int { + return len(pr.MustMarshal()) +} + +func (pr SchnorrPubRand) Marshal() ([]byte, error) { + return pr, nil +} + +func (pr SchnorrPubRand) MustMarshal() []byte { + prBytes, err := pr.Marshal() + if err != nil { + panic(err) + } + return prBytes +} + +func (pr SchnorrPubRand) MarshalTo(data []byte) (int, error) { + bz, err := pr.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +func (pr *SchnorrPubRand) Unmarshal(data []byte) error { + if len(data) != SchnorrPubRandLen { + return fmt.Errorf("invalid data length") + } + *pr = data + return nil +} + +func (pr *SchnorrPubRand) ToHexStr() string { + prBytes := pr.MustMarshal() + return hex.EncodeToString(prBytes) +} diff --git a/types/btc_schnorr_pub_rand_test.go b/types/btc_schnorr_pub_rand_test.go new file mode 100644 index 000000000..06e22387e --- /dev/null +++ b/types/btc_schnorr_pub_rand_test.go @@ -0,0 +1,35 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/stretchr/testify/require" +) + +func FuzzSchnorrPubRand(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + randBytes := datagen.GenRandomByteArray(r, 32) + var fieldVal btcec.FieldVal + fieldVal.SetByteSlice(randBytes) + + // FieldVal -> SchnorrPubRand -> FieldVal + pubRand := types.NewSchnorrPubRandFromFieldVal(&fieldVal) + fieldVal2 := pubRand.ToFieldVal() + require.True(t, fieldVal.Equals(fieldVal2)) + + // SchnorrPubRand -> bytes -> SchnorrPubRand + randBytes2 := pubRand.MustMarshal() + pubRand2, err := types.NewSchnorrPubRand(randBytes) + require.NoError(t, err) + require.Equal(t, randBytes, randBytes2) + require.Equal(t, pubRand, pubRand2) + }) +} diff --git a/types/btc_schnorr_sig.go b/types/btc_schnorr_sig.go new file mode 100644 index 000000000..58342323f --- /dev/null +++ b/types/btc_schnorr_sig.go @@ -0,0 +1,88 @@ +package types + +import ( + "encoding/hex" + "errors" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" +) + +type BIP340Signature []byte + +const BIP340SignatureLen = schnorr.SignatureSize + +func NewBIP340Signature(data []byte) (*BIP340Signature, error) { + var sig BIP340Signature + err := sig.Unmarshal(data) + return &sig, err +} + +func NewBIP340SignatureFromHex(sigHex string) (*BIP340Signature, error) { + sigBytes, err := hex.DecodeString(sigHex) + if err != nil { + return nil, err + } + return NewBIP340Signature(sigBytes) +} + +func NewBIP340SignatureFromBTCSig(btcSig *schnorr.Signature) *BIP340Signature { + sigBytes := btcSig.Serialize() + sig := BIP340Signature(sigBytes) + return &sig +} + +func (sig BIP340Signature) ToBTCSig() (*schnorr.Signature, error) { + return schnorr.ParseSignature(sig) +} + +func (sig BIP340Signature) MustToBTCSig() *schnorr.Signature { + btcSig, err := schnorr.ParseSignature(sig) + if err != nil { + panic(err) + } + return btcSig +} + +func (sig BIP340Signature) Size() int { + return len(sig.MustMarshal()) +} + +func (sig BIP340Signature) Marshal() ([]byte, error) { + return sig, nil +} + +func (sig BIP340Signature) MustMarshal() []byte { + sigBytes, err := sig.Marshal() + if err != nil { + panic(err) + } + return sigBytes +} + +func (sig BIP340Signature) MarshalTo(data []byte) (int, error) { + bz, err := sig.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +func (sig *BIP340Signature) Unmarshal(data []byte) error { + newSig := BIP340Signature(data) + + // ensure that the bytes can be transformed to a *schnorr.Signature object + // this includes all format checks + _, err := newSig.ToBTCSig() + if err != nil { + return errors.New("bytes cannot be converted to a *schnorr.Signature object") + } + + *sig = data + return nil +} + +func (sig *BIP340Signature) ToHexStr() string { + sigBytes := sig.MustMarshal() + return hex.EncodeToString(sigBytes) +} diff --git a/types/btc_schnorr_sig_test.go b/types/btc_schnorr_sig_test.go new file mode 100644 index 000000000..c8a7f2ad8 --- /dev/null +++ b/types/btc_schnorr_sig_test.go @@ -0,0 +1,41 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/stretchr/testify/require" +) + +func FuzzBIP340Signature(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + btcSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + // sign a random msg + msgHash := datagen.GenRandomBtcdHash(r) + btcSig, err := schnorr.Sign(btcSK, msgHash[:]) + require.NoError(t, err) + + // btcSig -> BIP340Signature -> btcSig + sig := types.NewBIP340SignatureFromBTCSig(btcSig) + btcSig2, err := sig.ToBTCSig() + require.NoError(t, err) + require.True(t, btcSig.IsEqual(btcSig2)) + + // BIP340Signature -> bytes -> BIP340Signature + sigBytes := sig.MustMarshal() + var sig2 types.BIP340Signature + _, err = types.NewBIP340Signature(sigBytes) + require.NoError(t, err) + err = sig2.Unmarshal(sigBytes) + require.NoError(t, err) + require.Equal(t, *sig, sig2) + }) +} diff --git a/types/btcutils.go b/types/btcutils.go index 4f4c08540..a9a8fca66 100644 --- a/types/btcutils.go +++ b/types/btcutils.go @@ -1,14 +1,16 @@ package types import ( + "bytes" + "encoding/hex" "errors" "fmt" "math/big" "time" "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/wire" ) // ValidateBTCHeader @@ -37,18 +39,6 @@ func ValidateBTCHeader(header *wire.BlockHeader, powLimit *big.Int) error { return nil } -func GetBaseBTCHeaderHex() string { - // TODO: get this from a configuration file - hex := "00006020c6c5a20e29da938a252c945411eba594cbeba021a1e20000000000000000000039e4bd0cd0b5232bb380a9576fcfe7d8fb043523f7a158187d9473e44c1740e6b4fa7c62ba01091789c24c22" - return hex -} - -func GetBaseBTCHeaderHeight() uint64 { - // TODO: get this from a configuration file - height := uint64(736056) - return height -} - func GetMaxDifficulty() big.Int { // Maximum btc difficulty possible // Use it to set the difficulty bits of blocks as well as the upper PoW limit @@ -62,11 +52,45 @@ func GetMaxDifficulty() big.Int { return *maxDifficulty } -func GetBaseBTCHeaderBytes() BTCHeaderBytes { - hex := GetBaseBTCHeaderHex() - headerBytes, err := NewBTCHeaderBytesFromHex(hex) +func NewBTCTxFromBytes(txBytes []byte) (*wire.MsgTx, error) { + var msgTx wire.MsgTx + rbuf := bytes.NewReader(txBytes) + if err := msgTx.Deserialize(rbuf); err != nil { + return nil, err + } + + return &msgTx, nil +} + +func NewBTCTxFromHex(txHex string) (*wire.MsgTx, []byte, error) { + txBytes, err := hex.DecodeString(txHex) + if err != nil { + return nil, nil, err + } + + parsed, err := NewBTCTxFromBytes(txBytes) + if err != nil { - panic("Base BTC header hex cannot be converted to bytes") + return nil, nil, err } - return headerBytes + + return parsed, txBytes, nil +} + +func SerializeBTCTx(tx *wire.MsgTx) ([]byte, error) { + var txBuf bytes.Buffer + if err := tx.Serialize(&txBuf); err != nil { + return nil, err + } + return txBuf.Bytes(), nil +} + +func GetOutputIdxInBTCTx(tx *wire.MsgTx, output *wire.TxOut) (uint32, error) { + for i, txOut := range tx.TxOut { + if bytes.Equal(txOut.PkScript, output.PkScript) && txOut.Value == output.Value { + return uint32(i), nil + } + } + + return 0, fmt.Errorf("output not found") } diff --git a/types/errors.go b/types/errors.go new file mode 100644 index 000000000..4ff369a35 --- /dev/null +++ b/types/errors.go @@ -0,0 +1,5 @@ +package types + +import "errors" + +var ErrUnmarshal = errors.New("unmarshal error") diff --git a/types/retry/log.go b/types/retry/log.go index 55c07f6bf..07b95b0f8 100644 --- a/types/retry/log.go +++ b/types/retry/log.go @@ -1,9 +1,9 @@ package retry import ( - "github.com/cometbft/cometbft/libs/log" + "cosmossdk.io/log" "os" ) // TODO add log formatters -var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) +var logger = log.NewLogger(os.Stdout) diff --git a/types/retry/retry.go b/types/retry/retry.go index ab66927f3..ec0845c15 100644 --- a/types/retry/retry.go +++ b/types/retry/retry.go @@ -13,6 +13,8 @@ import ( // unrecoverableErrors is a list of errors which are unsafe and should not be retried. var unrecoverableErrors = []error{ btclctypes.ErrHeaderParentDoesNotExist, + btclctypes.ErrChainWithNotEnoughWork, + btclctypes.ErrInvalidHeader, btcctypes.ErrProvidedHeaderDoesNotHaveAncestor, btcctypes.ErrInvalidHeader, btcctypes.ErrNoCheckpointsForPreviousEpoch, @@ -23,7 +25,6 @@ var unrecoverableErrors = []error{ // expectedErrors is a list of errors which can safely be ignored and should not be retried. var expectedErrors = []error{ - btclctypes.ErrDuplicateHeader, btcctypes.ErrDuplicatedSubmission, btcctypes.ErrInvalidHeader, // TODO Add more errors here diff --git a/wasmbinding/bindings/query.go b/wasmbinding/bindings/query.go index 7bef9606f..cc81ecbff 100644 --- a/wasmbinding/bindings/query.go +++ b/wasmbinding/bindings/query.go @@ -14,7 +14,7 @@ type BtcHeaderByHash struct { } type BtcHeaderByHeight struct { - Height uint64 `json:"height,omitempty"` + Height uint64 `json:"height"` } type CurrentEpochResponse struct { @@ -31,17 +31,17 @@ type FinalizedEpochInfo struct { } type BtcBlockHeader struct { - Version int32 `json:"version,omitempty"` + Version int32 `json:"version"` PrevBlockhash string `json:"prev_blockhash,omitempty"` MerkleRoot string `json:"merkle_root,omitempty"` - Time uint32 `json:"time,omitempty"` - Bits uint32 `json:"bits,omitempty"` - Nonce uint32 `json:"nonce,omitempty"` + Time uint32 `json:"time"` + Bits uint32 `json:"bits"` + Nonce uint32 `json:"nonce"` } type BtcBlockHeaderInfo struct { Header *BtcBlockHeader `json:"header,omitempty"` - Height uint64 `json:"height,omitempty"` + Height uint64 `json:"height"` } type BtcTipResponse struct { diff --git a/wasmbinding/bindings/utils.go b/wasmbinding/bindings/utils.go index 14f90d3f4..a4c278cd9 100644 --- a/wasmbinding/bindings/utils.go +++ b/wasmbinding/bindings/utils.go @@ -4,7 +4,7 @@ import ( lcTypes "github.com/babylonchain/babylon/x/btclightclient/types" ) -// translate BTCHeaderInfo to BtcBlockHeaderInfo +// AsBtcBlockHeaderInfo translates BTCHeaderInfo to BtcBlockHeaderInfo func AsBtcBlockHeaderInfo(info *lcTypes.BTCHeaderInfo) *BtcBlockHeaderInfo { if info == nil { return nil diff --git a/wasmbinding/test/custom_query_test.go b/wasmbinding/test/custom_query_test.go index 359da5b20..c52c84f8b 100644 --- a/wasmbinding/test/custom_query_test.go +++ b/wasmbinding/test/custom_query_test.go @@ -8,18 +8,20 @@ import ( "testing" "time" + "cosmossdk.io/math" "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmvmtypes "github.com/CosmWasm/wasmvm/types" - "github.com/babylonchain/babylon/app" - "github.com/babylonchain/babylon/testutil/datagen" - "github.com/babylonchain/babylon/wasmbinding/bindings" "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/ed25519" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/wasmbinding/bindings" ) // TODO consider doing it by enviromental variables as currently it may fail on some @@ -48,7 +50,7 @@ func TestQueryEpoch(t *testing.T) { } resp := bindings.CurrentEpochResponse{} queryCustom(t, ctx, babylonApp, contractAddress, query, &resp) - require.Equal(t, resp.Epoch, uint64(0)) + require.Equal(t, resp.Epoch, uint64(1)) newEpoch := babylonApp.EpochingKeeper.IncEpoch(ctx) @@ -199,7 +201,8 @@ func setupAppWithContext(t *testing.T) (*app.BabylonApp, sdk.Context) { func setupAppWithContextAndCustomHeight(t *testing.T, height int64) (*app.BabylonApp, sdk.Context) { babylonApp := app.Setup(t, false) - ctx := babylonApp.BaseApp.NewContext(false, tmproto.Header{Height: height, Time: time.Now().UTC()}) + ctx := babylonApp.BaseApp.NewContext(false). + WithBlockHeader(cmtproto.Header{Height: height, Time: time.Now().UTC()}) return babylonApp, ctx } @@ -234,7 +237,7 @@ func fundAccount( acc sdk.AccAddress) { err := mintCoinsTo(bbn.BankKeeper, ctx, acc, sdk.NewCoins( - sdk.NewCoin("ubbn", sdk.NewInt(10000000000)), + sdk.NewCoin("ubbn", math.NewInt(10000000000)), )) require.NoError(t, err) } diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go index 41e78cff5..c96ecd884 100644 --- a/wasmbinding/wasm.go +++ b/wasmbinding/wasm.go @@ -5,7 +5,6 @@ import ( "fmt" errorsmod "cosmossdk.io/errors" - "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmvmtypes "github.com/CosmWasm/wasmvm/types" bbn "github.com/babylonchain/babylon/types" @@ -168,7 +167,7 @@ func RegisterCustomPlugins( Custom: CustomQuerier(wasmQueryPlugin), }) - return []wasm.Option{ + return []wasmkeeper.Option{ queryPluginOpt, } } diff --git a/x/btccheckpoint/abci.go b/x/btccheckpoint/abci.go index 34136c669..2307557f2 100644 --- a/x/btccheckpoint/abci.go +++ b/x/btccheckpoint/abci.go @@ -1,15 +1,14 @@ package btccheckpoint import ( + "context" "github.com/babylonchain/babylon/x/btccheckpoint/keeper" - abci "github.com/cometbft/cometbft/abci/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // EndBlocker checks if during block execution btc light client head had been // updated. If the head had been updated, status of all available checkpoints // is checked to determine if any of them became confirmed/finalized/abandonded. -func EndBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestEndBlock) { +func EndBlocker(ctx context.Context, k keeper.Keeper) { if k.BtcLightClientUpdated(ctx) { k.OnTipChange(ctx) } diff --git a/x/btccheckpoint/client/cli/query.go b/x/btccheckpoint/client/cli/query.go index 73696536e..49a2376cd 100644 --- a/x/btccheckpoint/client/cli/query.go +++ b/x/btccheckpoint/client/cli/query.go @@ -97,5 +97,7 @@ func CmdEpochSubmissions() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "epoch-submissions") + return cmd } diff --git a/x/btccheckpoint/client/cli/tx.go b/x/btccheckpoint/client/cli/tx.go index 752ae5679..9d9f5b608 100644 --- a/x/btccheckpoint/client/cli/tx.go +++ b/x/btccheckpoint/client/cli/tx.go @@ -2,8 +2,6 @@ package cli import ( "fmt" - "time" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -14,16 +12,6 @@ import ( "github.com/babylonchain/babylon/x/btccheckpoint/types" ) -var ( - DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) -) - -//nolint:unused -const ( - flagPacketTimeoutTimestamp = "packet-timeout-timestamp" - listSeparator = "," -) - // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/btccheckpoint/genesis.go b/x/btccheckpoint/genesis.go index a5246537b..7b55f84a0 100644 --- a/x/btccheckpoint/genesis.go +++ b/x/btccheckpoint/genesis.go @@ -1,14 +1,14 @@ package btccheckpoint import ( + "context" "github.com/babylonchain/babylon/x/btccheckpoint/keeper" "github.com/babylonchain/babylon/x/btccheckpoint/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { // set params for this module if err := k.SetParams(ctx, genState.Params); err != nil { panic(err) @@ -16,7 +16,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) } // ExportGenesis returns the capability module's exported genesis. -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() genesis.Params = k.GetParams(ctx) diff --git a/x/btccheckpoint/genesis_test.go b/x/btccheckpoint/genesis_test.go index da1d512d9..5266094df 100644 --- a/x/btccheckpoint/genesis_test.go +++ b/x/btccheckpoint/genesis_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/babylonchain/babylon/x/btccheckpoint" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/stretchr/testify/require" simapp "github.com/babylonchain/babylon/app" @@ -13,7 +12,7 @@ import ( func TestExportGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) if err := app.BtcCheckpointKeeper.SetParams(ctx, types.DefaultParams()); err != nil { panic(err) @@ -25,7 +24,7 @@ func TestExportGenesis(t *testing.T) { func TestInitGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) genesisState := types.GenesisState{ Params: types.Params{ diff --git a/x/btccheckpoint/keeper/grpc_query.go b/x/btccheckpoint/keeper/grpc_query.go index 19c038344..d5e155029 100644 --- a/x/btccheckpoint/keeper/grpc_query.go +++ b/x/btccheckpoint/keeper/grpc_query.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/babylonchain/babylon/x/btccheckpoint/types" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" @@ -15,7 +14,7 @@ import ( var _ types.QueryServer = Keeper{} -func (k Keeper) getCheckpointInfo(ctx sdk.Context, epochNum uint64, epochData *types.EpochData) (*types.BTCCheckpointInfo, error) { +func (k Keeper) getCheckpointInfo(ctx context.Context, epochNum uint64, epochData *types.EpochData) (*types.BTCCheckpointInfo, error) { bestSubmission := k.GetEpochBestSubmissionBtcInfo(ctx, epochData) if bestSubmission == nil { @@ -62,15 +61,12 @@ func (k Keeper) BtcCheckpointInfo(c context.Context, req *types.QueryBtcCheckpoi return resp, nil } -func (k Keeper) BtcCheckpointsInfo(c context.Context, req *types.QueryBtcCheckpointsInfoRequest) (*types.QueryBtcCheckpointsInfoResponse, error) { +func (k Keeper) BtcCheckpointsInfo(ctx context.Context, req *types.QueryBtcCheckpointsInfoRequest) (*types.QueryBtcCheckpointsInfoResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } - ctx := sdk.UnwrapSDKContext(c) - - store := ctx.KVStore(k.storeKey) - epochDataStore := prefix.NewStore(store, types.EpochDataPrefix) + epochDataStore := k.epochDataStore(ctx) ckptInfoList := []*types.BTCCheckpointInfo{} // iterate over epochDataStore, where key is the epoch number and value is the epoch data @@ -139,7 +135,7 @@ func (k Keeper) EpochSubmissions(c context.Context, req *types.QueryEpochSubmiss epochData := k.GetEpochData(ctx, checkpointEpoch) - if epochData == nil || len(epochData.Key) == 0 { + if epochData == nil || len(epochData.Keys) == 0 { return &types.QueryEpochSubmissionsResponse{ Keys: []*types.SubmissionKey{}, @@ -147,7 +143,7 @@ func (k Keeper) EpochSubmissions(c context.Context, req *types.QueryEpochSubmiss }, nil } - numberOfKeys := uint64(len((epochData.Key))) + numberOfKeys := uint64(len((epochData.Keys))) if offset >= numberOfKeys { // offset larger than number of keys return empty response @@ -164,7 +160,7 @@ func (k Keeper) EpochSubmissions(c context.Context, req *types.QueryEpochSubmiss break } - responseKeys = append(responseKeys, epochData.Key[i]) + responseKeys = append(responseKeys, epochData.Keys[i]) } return &types.QueryEpochSubmissionsResponse{ diff --git a/x/btccheckpoint/keeper/grpc_query_params_test.go b/x/btccheckpoint/keeper/grpc_query_params_test.go index ac8e19de5..b9db61836 100644 --- a/x/btccheckpoint/keeper/grpc_query_params_test.go +++ b/x/btccheckpoint/keeper/grpc_query_params_test.go @@ -6,7 +6,6 @@ import ( testkeeper "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/x/btccheckpoint/types" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) @@ -52,8 +51,7 @@ func FuzzParamsQuery(f *testing.F) { params.BtcConfirmationDepth = checkpointFinalizationTimeout } - keeper, ctx := testkeeper.NewBTCCheckpointKeeper(t, nil, nil, nil) - wctx := sdk.WrapSDKContext(ctx) + keeper, ctx := testkeeper.NewBTCCheckpointKeeper(t, nil, nil, nil, nil) // if setParamsFlag == 0, set params setParamsFlag := r.Intn(2) @@ -63,7 +61,7 @@ func FuzzParamsQuery(f *testing.F) { } } req := types.QueryParamsRequest{} - resp, err := keeper.Params(wctx, &req) + resp, err := keeper.Params(ctx, &req) require.NoError(t, err) // if setParamsFlag == 0, resp.Params should be changed, otherwise default if setParamsFlag == 0 { diff --git a/x/btccheckpoint/keeper/hooks.go b/x/btccheckpoint/keeper/hooks.go index 126e49fbd..3fd096a46 100644 --- a/x/btccheckpoint/keeper/hooks.go +++ b/x/btccheckpoint/keeper/hooks.go @@ -1,12 +1,13 @@ package keeper import ( + "context" ltypes "github.com/babylonchain/babylon/x/btclightclient/types" etypes "github.com/babylonchain/babylon/x/epoching/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) -// Helper interface to be sure Hooks implement both epoching and light client hooks +// HandledHooks Helper interface to ensure Hooks implements +// both epoching and btclightclient hooks type HandledHooks interface { ltypes.BTCLightClientHooks etypes.EpochingHooks @@ -20,18 +21,18 @@ var _ HandledHooks = Hooks{} func (k Keeper) Hooks() Hooks { return Hooks{k} } -func (h Hooks) AfterBTCRollBack(ctx sdk.Context, headerInfo *ltypes.BTCHeaderInfo) { +func (h Hooks) AfterBTCRollBack(ctx context.Context, _ *ltypes.BTCHeaderInfo) { h.k.setBtcLightClientUpdated(ctx) } -func (h Hooks) AfterBTCRollForward(ctx sdk.Context, headerInfo *ltypes.BTCHeaderInfo) { +func (h Hooks) AfterBTCRollForward(ctx context.Context, _ *ltypes.BTCHeaderInfo) { h.k.setBtcLightClientUpdated(ctx) } -func (h Hooks) AfterBTCHeaderInserted(ctx sdk.Context, headerInfo *ltypes.BTCHeaderInfo) {} +func (h Hooks) AfterBTCHeaderInserted(_ context.Context, _ *ltypes.BTCHeaderInfo) {} -func (h Hooks) AfterEpochBegins(ctx sdk.Context, epoch uint64) {} +func (h Hooks) AfterEpochBegins(_ context.Context, _ uint64) {} -func (h Hooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) {} +func (h Hooks) AfterEpochEnds(_ context.Context, _ uint64) {} -func (h Hooks) BeforeSlashThreshold(ctx sdk.Context, valSet etypes.ValidatorSet) {} +func (h Hooks) BeforeSlashThreshold(_ context.Context, _ etypes.ValidatorSet) {} diff --git a/x/btccheckpoint/keeper/incentive.go b/x/btccheckpoint/keeper/incentive.go new file mode 100644 index 000000000..36c17f035 --- /dev/null +++ b/x/btccheckpoint/keeper/incentive.go @@ -0,0 +1,44 @@ +package keeper + +import ( + "context" + "github.com/babylonchain/babylon/x/btccheckpoint/types" +) + +// rewardBTCTimestamping finds the (submitter, reporter) pairs of all submissions at the +// given finalised epoch according to the given epoch data, then distribute rewards to them +// by invoking the incentive module +func (k Keeper) rewardBTCTimestamping(ctx context.Context, epoch uint64, ed *types.EpochData, bestIdx int) { + var ( + bestSubmissionAddrs *types.CheckpointAddressPair + otherSubmissionAddrs []*types.CheckpointAddressPair + ) + + // iterate over all submission keys to find all submission addresses, including the best one + for i, sk := range ed.Keys { + // retrieve submission data, including vigilante addresses + submissionData := k.GetSubmissionData(ctx, *sk) + if submissionData == nil { + // ignore nil submission data for whatever reason + continue + } + + // get vigilante addresses of this submission + submissionAddrs, err := types.NewCheckpointAddressPair(submissionData.VigilanteAddresses) + if err != nil { + // failing to unmarshal checkpoint address pair in KVStore is a programming error + panic(err) + } + + // assign to best submission or append to other submission according to best submission index + if i == bestIdx { + bestSubmissionAddrs = submissionAddrs + } else { + otherSubmissionAddrs = append(otherSubmissionAddrs, submissionAddrs) + } + } + + // construct reward distribution information and invoke incentive module to distribute rewards + rewardDistInfo := types.NewRewardDistInfo(bestSubmissionAddrs, otherSubmissionAddrs...) + k.incentiveKeeper.RewardBTCTimestamping(ctx, epoch, rewardDistInfo) +} diff --git a/x/btccheckpoint/keeper/keeper.go b/x/btccheckpoint/keeper/keeper.go index 10753584a..7f01dad7a 100644 --- a/x/btccheckpoint/keeper/keeper.go +++ b/x/btccheckpoint/keeper/keeper.go @@ -1,29 +1,31 @@ package keeper import ( + "context" + corestoretypes "cosmossdk.io/core/store" + storetypes "cosmossdk.io/store/types" "encoding/hex" "fmt" - "math" + "github.com/cosmos/cosmos-sdk/runtime" "math/big" + "cosmossdk.io/log" + "cosmossdk.io/store/prefix" txformat "github.com/babylonchain/babylon/btctxformatter" bbn "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btccheckpoint/types" - "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) type ( Keeper struct { cdc codec.BinaryCodec - storeKey storetypes.StoreKey - tstoreKey storetypes.StoreKey - memKey storetypes.StoreKey + storeService corestoretypes.KVStoreService + tsKey storetypes.StoreKey btcLightClientKeeper types.BTCLightClientKeeper checkpointingKeeper types.CheckpointingKeeper + incentiveKeeper types.IncentiveKeeper powLimit *big.Int authority string } @@ -51,30 +53,26 @@ const ( submissionUnknownErr submissionBtcError = submissionBtcError( "One of submission blocks is not known to btclightclient", ) - - subbmisionOnForkErr submissionBtcError = submissionBtcError( - "One of submission blocks is not on the btc mainchain ", - ) ) func NewKeeper( cdc codec.BinaryCodec, - storeKey, - tstoreKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, + tsKey storetypes.StoreKey, bk types.BTCLightClientKeeper, ck types.CheckpointingKeeper, + ik types.IncentiveKeeper, powLimit *big.Int, authority string, ) Keeper { return Keeper{ cdc: cdc, - storeKey: storeKey, - tstoreKey: tstoreKey, - memKey: memKey, + storeService: storeService, + tsKey: tsKey, btcLightClientKeeper: bk, checkpointingKeeper: ck, + incentiveKeeper: ik, powLimit: powLimit, authority: authority, } @@ -88,7 +86,7 @@ func (k Keeper) GetPowLimit() *big.Int { // hex string to bytes. // NOTE: keeper could probably cache decoded tag, but it is rather improbable this function // will ever be a bottleneck so it is not worth it. -func (k Keeper) GetExpectedTag(ctx sdk.Context) txformat.BabylonTag { +func (k Keeper) GetExpectedTag(ctx context.Context) txformat.BabylonTag { tag := k.GetParams(ctx).CheckpointTag tagAsBytes, err := hex.DecodeString(tag) @@ -104,169 +102,26 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) GetBlockHeight(ctx sdk.Context, b *bbn.BTCHeaderHashBytes) (uint64, error) { +func (k Keeper) GetBlockHeight(ctx context.Context, b *bbn.BTCHeaderHashBytes) (uint64, error) { return k.btcLightClientKeeper.BlockHeight(ctx, b) } -func (k Keeper) headerDepth(ctx sdk.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) { +func (k Keeper) headerDepth(ctx context.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) { blockDepth, err := k.btcLightClientKeeper.MainChainDepth(ctx, headerHash) if err != nil { // one of blocks is not known to light client return 0, submissionUnknownErr } - - if blockDepth < 0 { - // one of submission blocks is on fork, treat whole submission as being on fork - return 0, subbmisionOnForkErr - } - return uint64(blockDepth), nil } -func (k Keeper) checkSubmissionStatus(ctx sdk.Context, info *types.SubmissionBtcInfo) types.BtcStatus { - subDepth := info.SubmissionDepth() - if subDepth >= k.GetParams(ctx).CheckpointFinalizationTimeout { - return types.Finalized - } else if subDepth >= k.GetParams(ctx).BtcConfirmationDepth { - return types.Confirmed - } else { - return types.Submitted - } -} - -func (k Keeper) GetSubmissionBtcInfo(ctx sdk.Context, sk types.SubmissionKey) (*types.SubmissionBtcInfo, error) { - - var youngestBlockDepth uint64 = math.MaxUint64 - var youngestBlockHash *bbn.BTCHeaderHashBytes - - var lowestIndexInMostFreshBlock uint32 = math.MaxUint32 - - var oldestBlockDepth uint64 = uint64(0) - - for _, tk := range sk.Key { - currentBlockDepth, err := k.headerDepth(ctx, tk.Hash) - - if err != nil { - return nil, err - } - - if currentBlockDepth < youngestBlockDepth { - youngestBlockDepth = currentBlockDepth - lowestIndexInMostFreshBlock = tk.Index - youngestBlockHash = tk.Hash - } - - // This case happens when we have two submissions in the same block. - if currentBlockDepth == youngestBlockDepth && tk.Index < lowestIndexInMostFreshBlock { - // This is something which needs a bit more careful thinking as it is used - // to determine which submission is better. - // Currently if two submissions of one checkpoint are in the same block, - // we pick tx with lower index as the point at which checkpoint happened. - // This is in line with the logic that if two submission are in the same block, - // they are esentially happening at the same time, so it does not really matter - // which index pick, and for possibble tie breaks it is better to pick lower one. - // This means in case when we have: - // Checkpoint submission `x` for epoch 5, both tx in same block at height 100, with indexes 1 and 10 - // and - // Checkpoint submission `y` for epoch 5, both tx in same block at height 100, with indexes 3 and 9 - // we will chose submission `x` as the better one. - // This good enough solution, but it is not perfect and leads to some edge cases like: - // Checkpoint submission `x` for epoch 5, one tx in block 99 with index 1, and second tx in block 100 with index 4 - // and - // Checkpoint submission `y` for epoch 5, both tx in same block at height 100, with indexes 3 and 9 - // In this case submission `y` will be better as it `earliest` tx in most fresh block is first. But at first glance - // submission `x` seems better. - lowestIndexInMostFreshBlock = tk.Index - } - - if currentBlockDepth > oldestBlockDepth { - oldestBlockDepth = currentBlockDepth - } - } - - return &types.SubmissionBtcInfo{ - SubmissionKey: sk, - OldestBlockDepth: oldestBlockDepth, - YoungestBlockDepth: youngestBlockDepth, - YoungestBlockHash: *youngestBlockHash, - YoungestBlockLowestTxIdx: lowestIndexInMostFreshBlock, - }, nil -} - -func (k Keeper) SubmissionExists(ctx sdk.Context, sk types.SubmissionKey) bool { - return k.GetSubmissionData(ctx, sk) != nil -} - -// GetEpochData returns epoch data for given epoch, if there is not epoch data yet returns nil -func (k Keeper) GetEpochData(ctx sdk.Context, e uint64) *types.EpochData { - store := ctx.KVStore(k.storeKey) - bytes := store.Get(types.GetEpochIndexKey(e)) - - // note: Cannot check len(bytes) == 0, as empty bytes encoding of types.EpochData - // is epoch data with Status == Submitted and no valid submissions - if bytes == nil { - return nil - } - - ed := &types.EpochData{} - k.cdc.MustUnmarshal(bytes, ed) - return ed -} - -// GetBestSubmission gets the status and the best submission of a given finalized epoch -func (k Keeper) GetBestSubmission(ctx sdk.Context, epochNumber uint64) (types.BtcStatus, *types.SubmissionKey, error) { - // find the btc checkpoint tx index of this epoch - ed := k.GetEpochData(ctx, epochNumber) - if ed == nil { - return 0, nil, types.ErrNoCheckpointsForPreviousEpoch - } - if ed.Status != types.Finalized { - return 0, nil, fmt.Errorf("epoch %d has not been finalized yet", epochNumber) - } - if len(ed.Key) == 0 { - return 0, nil, types.ErrNoCheckpointsForPreviousEpoch - } - bestSubmissionKey := ed.Key[0] // index of checkpoint tx on BTC - - return ed.Status, bestSubmissionKey, nil -} - -func (k Keeper) GetEpochBestSubmissionBtcInfo(ctx sdk.Context, ed *types.EpochData) *types.SubmissionBtcInfo { - // there is no submissions for this epoch, so transitivly there is no best submission - if ed == nil || len(ed.Key) == 0 { - return nil - } - - // There is only one submission for this epoch: - // - either epoch is already finalized and we already chosen the best submission - // - or we only received one submission for this epoch - // Either way, we do not need to decide which submission is the best one. - if len(ed.Key) == 1 { - sk := *ed.Key[0] - btcInfo, err := k.GetSubmissionBtcInfo(ctx, sk) - - if err != nil { - k.Logger(ctx).Debug("Previously stored submission is not valid anymore. Submission key: %+v", sk) - } - - // we only log error, as the only error which we can receive here is that submission - // is not longer on btc canoncial chain, which essentially means that there is no valid submission - return btcInfo - } - - // We have more that one valid submission. We need to chose the best one. - epochSummary := k.getEpochChanges(ctx, nil, ed) - - return epochSummary.EpochBestSubmission -} - // checkAncestors checks if there is at least one ancestor in previous epoch submissions // previous epoch submission is considered ancestor when: // - it is on main chain // - its lowest depth is larger than highest depth of new submission func (k Keeper) checkAncestors( - ctx sdk.Context, + ctx context.Context, submisionEpoch uint64, newSubmissionInfo *types.SubmissionBtcInfo, ) error { @@ -285,13 +140,13 @@ func (k Keeper) checkAncestors( return types.ErrNoCheckpointsForPreviousEpoch } - if len(previousEpochData.Key) == 0 { + if len(previousEpochData.Keys) == 0 { return types.ErrNoCheckpointsForPreviousEpoch } var haveDescendant = false - for _, sk := range previousEpochData.Key { + for _, sk := range previousEpochData.Keys { if len(sk.Key) < 2 { panic("Submission key composed of less than 2 transactions keys in database") } @@ -320,110 +175,27 @@ func (k Keeper) checkAncestors( return nil } -func (k Keeper) saveEpochData(ctx sdk.Context, e uint64, ed *types.EpochData) { - store := ctx.KVStore(k.storeKey) - ek := types.GetEpochIndexKey(e) - eb := k.cdc.MustMarshal(ed) - store.Set(ek, eb) -} - -// addEpochSubmission save given submission key and data to database and takes -// car of updating any necessary indexes. -// Provided submmission should be known to btclightclient and all of its blocks -// should be on btc main chaing as viewed by btclightclient -func (k Keeper) addEpochSubmission( - ctx sdk.Context, - epochNum uint64, - sk types.SubmissionKey, - sd types.SubmissionData, -) error { - - ed := k.GetEpochData(ctx, epochNum) - - // TODO: SaveEpochData and SaveSubmission should be done in one transaction. - // Not sure cosmos-sdk has facialities to do it. - // Otherwise it is possible to end up with node which updated submission list - // but did not save submission itself. - - // if ed is nil, it means it is our first submission for this epoch - if ed == nil { - // we do not have any data saved yet - newEd := types.NewEmptyEpochData() - ed = &newEd - } - - if ed.Status == types.Finalized { - // we already finlized given epoch so we do not need any more submissions - // TODO We should probably compare new submmission with the exisiting submission - // which finalized the epoch. As it means we finalized epoch with not the best - // submission possible - return types.ErrEpochAlreadyFinalized - } - - if len(ed.Key) == 0 { - // it is first epoch submission inform checkpointing module about this fact - k.checkpointingKeeper.SetCheckpointSubmitted(ctx, epochNum) - } - - ed.AppendKey(sk) - k.saveEpochData(ctx, epochNum, ed) - k.saveSubmission(ctx, sk, sd) - return nil -} - -func (k Keeper) saveSubmission(ctx sdk.Context, sk types.SubmissionKey, sd types.SubmissionData) { - store := ctx.KVStore(k.storeKey) - kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) - sBytes := k.cdc.MustMarshal(&sd) - store.Set(kBytes, sBytes) -} - -func (k Keeper) deleteSubmission(ctx sdk.Context, sk types.SubmissionKey) { - store := ctx.KVStore(k.storeKey) - kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) - store.Delete(kBytes) -} - -// GetSubmissionData returns submission data for a given key or nil if there is no data -// under the given key -func (k Keeper) GetSubmissionData(ctx sdk.Context, sk types.SubmissionKey) *types.SubmissionData { - store := ctx.KVStore(k.storeKey) - kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) - sdBytes := store.Get(kBytes) - - if len(sdBytes) == 0 { - return nil - } - - var sd types.SubmissionData - k.cdc.MustUnmarshal(sdBytes, &sd) - return &sd -} - -// Callback to be called when btc light client tip changes -func (k Keeper) OnTipChange(ctx sdk.Context) { - k.checkCheckpoints(ctx) -} - -func (k Keeper) setBtcLightClientUpdated(ctx sdk.Context) { - store := ctx.TransientStore(k.tstoreKey) +func (k Keeper) setBtcLightClientUpdated(ctx context.Context) { + store := sdk.UnwrapSDKContext(ctx).TransientStore(k.tsKey) store.Set(types.GetBtcLightClientUpdatedKey(), []byte{1}) } // BtcLightClientUpdated checks if btc light client was updated during block execution -func (k Keeper) BtcLightClientUpdated(ctx sdk.Context) bool { +func (k Keeper) BtcLightClientUpdated(ctx context.Context) bool { // transient store is cleared after each block execution, therfore if // BtcLightClientKey is set, it means setBtcLightClientUpdated was called during // current block execution - store := ctx.TransientStore(k.tstoreKey) + store := sdk.UnwrapSDKContext(ctx).TransientStore(k.tsKey) lcUpdated := store.Get(types.GetBtcLightClientUpdatedKey()) return len(lcUpdated) > 0 } -func (k Keeper) getLastFinalizedEpochNumber(ctx sdk.Context) uint64 { - store := ctx.KVStore(k.storeKey) - epoch := store.Get(types.GetLatestFinalizedEpochKey()) - +func (k Keeper) getLastFinalizedEpochNumber(ctx context.Context) uint64 { + store := k.storeService.OpenKVStore(ctx) + epoch, err := store.Get(types.GetLatestFinalizedEpochKey()) + if err != nil { + panic(err) + } if len(epoch) == 0 { return uint64(0) } @@ -431,13 +203,15 @@ func (k Keeper) getLastFinalizedEpochNumber(ctx sdk.Context) uint64 { return sdk.BigEndianToUint64(epoch) } -func (k Keeper) setLastFinalizedEpochNumber(ctx sdk.Context, epoch uint64) { - store := ctx.KVStore(k.storeKey) - store.Set(types.GetLatestFinalizedEpochKey(), sdk.Uint64ToBigEndian(epoch)) +func (k Keeper) setLastFinalizedEpochNumber(ctx context.Context, epoch uint64) { + store := k.storeService.OpenKVStore(ctx) + if err := store.Set(types.GetLatestFinalizedEpochKey(), sdk.Uint64ToBigEndian(epoch)); err != nil { + panic(err) + } } func (k Keeper) getEpochChanges( - ctx sdk.Context, + ctx context.Context, parentEpochBestSubmission *types.SubmissionBtcInfo, ed *types.EpochData) *epochChangesSummary { @@ -446,7 +220,7 @@ func (k Keeper) getEpochChanges( var currentEpochBestSubmission *types.SubmissionBtcInfo var bestSubmissionIdx int - for i, sk := range ed.Key { + for i, sk := range ed.Keys { sk := sk if len(sk.Key) < 2 { panic("Submission key composed of less than 2 transactions keys in database") @@ -494,16 +268,9 @@ func (k Keeper) getEpochChanges( } } -func (k Keeper) clearEpochData( - ctx sdk.Context, - epoch []byte, - epochDataStore prefix.Store, - currentEpoch *types.EpochData) { - for _, sk := range currentEpoch.Key { - k.deleteSubmission(ctx, *sk) - } - currentEpoch.Key = []*types.SubmissionKey{} - epochDataStore.Set(epoch, k.cdc.MustMarshal(currentEpoch)) +// OnTipChange is the callback function to be called when btc light client tip changes +func (k Keeper) OnTipChange(ctx context.Context) { + k.checkCheckpoints(ctx) } // checkCheckpoints is the main function checking status of all submissions @@ -536,11 +303,8 @@ func (k Keeper) clearEpochData( // 7. If the epoch loses all of its submissions, delete all submissions from child // epoch as then we do not have parent for those. -func (k Keeper) checkCheckpoints(ctx sdk.Context) { - store := prefix.NewStore( - ctx.KVStore(k.storeKey), - types.EpochDataPrefix, - ) +func (k Keeper) checkCheckpoints(ctx context.Context) { + store := k.epochDataStore(ctx) lastFinalizedEpoch := k.getLastFinalizedEpochNumber(ctx) @@ -562,7 +326,7 @@ func (k Keeper) checkCheckpoints(ctx sdk.Context) { k.cdc.MustUnmarshal(it.Value(), ¤tEpoch) epoch := sdk.BigEndianToUint64(it.Key()) - if len(currentEpoch.Key) == 0 { + if len(currentEpoch.Keys) == 0 { // current epoch does not have any submissions, so following one should also // not have any submissions, stop the processing. break @@ -571,11 +335,11 @@ func (k Keeper) checkCheckpoints(ctx sdk.Context) { if currentEpoch.Status == types.Finalized { // current epoch is already finalized. This is our first epoch in iteration // just set parent info - if len(currentEpoch.Key) != 1 { + if len(currentEpoch.Keys) != 1 { panic("Finalized epoch must have only one valid submission") } - subInfo, err := k.GetSubmissionBtcInfo(ctx, *currentEpoch.Key[0]) + subInfo, err := k.GetSubmissionBtcInfo(ctx, *currentEpoch.Keys[0]) if err != nil { panic("Finalized epoch submission must be on main chain") @@ -631,7 +395,6 @@ func (k Keeper) checkCheckpoints(ctx sdk.Context) { // epoch just got confirmed by best submission currentEpoch.Status = types.Confirmed k.checkpointingKeeper.SetCheckpointConfirmed(ctx, epoch) - // TODO This is the place to check other submissions and pay up rewards. } if bestSubmissionStatus > currentEpoch.Status && currentEpoch.Status == types.Confirmed { @@ -642,20 +405,22 @@ func (k Keeper) checkCheckpoints(ctx sdk.Context) { } if currentEpoch.Status == types.Finalized { - for i, sk := range currentEpoch.Key { - // delete all submissions except best one + // trigger incentive module to distribute rewards to submitters/reporters + k.rewardBTCTimestamping(ctx, epoch, ¤tEpoch, epochChanges.BestSubmissionIdx) + // delete all submissions except best one + for i, sk := range currentEpoch.Keys { if i != epochChanges.BestSubmissionIdx { k.deleteSubmission(ctx, *sk) } } // leave only best submission key - currentEpoch.Key = []*types.SubmissionKey{&epochChanges.EpochBestSubmission.SubmissionKey} + currentEpoch.Keys = []*types.SubmissionKey{&epochChanges.EpochBestSubmission.SubmissionKey} } else { // apply changes to epoch according to changes for _, sk := range epochChanges.SubmissionsToDelete { k.deleteSubmission(ctx, *sk) } - currentEpoch.Key = epochChanges.SubmissionsToKeep + currentEpoch.Keys = epochChanges.SubmissionsToKeep } parentEpochInfo = &epochInfo{bestSubmission: epochChanges.EpochBestSubmission} @@ -663,3 +428,8 @@ func (k Keeper) checkCheckpoints(ctx sdk.Context) { store.Set(it.Key(), k.cdc.MustMarshal(¤tEpoch)) } } + +func (k *Keeper) epochDataStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.EpochDataPrefix) +} diff --git a/x/btccheckpoint/keeper/keeper_test.go b/x/btccheckpoint/keeper/keeper_test.go index 97a360232..5eed1abd6 100644 --- a/x/btccheckpoint/keeper/keeper_test.go +++ b/x/btccheckpoint/keeper/keeper_test.go @@ -49,8 +49,8 @@ func TestKeeper_GetSubmissionBtcInfo(t *testing.T) { {Index: tt.args.Key2.TxIdx, Hash: hash2}, }} - k.BTCLightClient.SetDepth(hash1, int64(tt.args.Key1.Depth)) - k.BTCLightClient.SetDepth(hash2, int64(tt.args.Key2.Depth)) + k.BTCLightClient.SetDepth(hash1, tt.args.Key1.Depth) + k.BTCLightClient.SetDepth(hash2, tt.args.Key2.Depth) info, err := k.BTCCheckpoint.GetSubmissionBtcInfo(k.SdkCtx, sk) @@ -90,8 +90,8 @@ func FuzzGetSubmissionBtcInfo(f *testing.F) { {Index: txidx2, Hash: hash2}, }} - k.BTCLightClient.SetDepth(hash1, int64(depth1)) - k.BTCLightClient.SetDepth(hash2, int64(depth2)) + k.BTCLightClient.SetDepth(hash1, uint64(depth1)) + k.BTCLightClient.SetDepth(hash2, uint64(depth2)) info, err := k.BTCCheckpoint.GetSubmissionBtcInfo(k.SdkCtx, sk) require.NoError(t, err) diff --git a/x/btccheckpoint/keeper/msg_server.go b/x/btccheckpoint/keeper/msg_server.go index 08906227e..17480a698 100644 --- a/x/btccheckpoint/keeper/msg_server.go +++ b/x/btccheckpoint/keeper/msg_server.go @@ -24,12 +24,12 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { // TODO at some point add proper logging of error // TODO emit some events for external consumers. Those should be probably emited // at EndBlockerCallback -func (m msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBTCSpvProof) (*types.MsgInsertBTCSpvProofResponse, error) { +func (ms msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBTCSpvProof) (*types.MsgInsertBTCSpvProofResponse, error) { // Get the SDK wrapped context sdkCtx := sdk.UnwrapSDKContext(ctx) - rawSubmission, err := types.ParseSubmission(req, m.k.GetPowLimit(), m.k.GetExpectedTag(sdkCtx)) + rawSubmission, err := types.ParseSubmission(req, ms.k.GetPowLimit(), ms.k.GetExpectedTag(sdkCtx)) if err != nil { return nil, types.ErrInvalidCheckpointProof.Wrap(err.Error()) @@ -37,11 +37,11 @@ func (m msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBT submissionKey := rawSubmission.GetSubmissionKey() - if m.k.SubmissionExists(sdkCtx, submissionKey) { + if ms.k.HasSubmission(sdkCtx, submissionKey) { return nil, types.ErrDuplicatedSubmission } - newSubmissionOldestHeaderDepth, err := m.k.GetSubmissionBtcInfo(sdkCtx, submissionKey) + newSubmissionOldestHeaderDepth, err := ms.k.GetSubmissionBtcInfo(sdkCtx, submissionKey) if err != nil { return nil, types.ErrInvalidHeader.Wrap(err.Error()) @@ -53,7 +53,7 @@ func (m msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBT // - header is proved to be part of the chain we know about through BTCLightClient // - this is new checkpoint submission // Verify if this is expected checkpoint - err = m.k.checkpointingKeeper.VerifyCheckpoint(sdkCtx, rawSubmission.CheckpointData) + err = ms.k.checkpointingKeeper.VerifyCheckpoint(sdkCtx, rawSubmission.CheckpointData) if err != nil { return nil, err @@ -63,7 +63,7 @@ func (m msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBT // by checkpointing module epochNum := rawSubmission.CheckpointData.Epoch - err = m.k.checkAncestors(sdkCtx, epochNum, newSubmissionOldestHeaderDepth) + err = ms.k.checkAncestors(sdkCtx, epochNum, newSubmissionOldestHeaderDepth) if err != nil { return nil, err @@ -81,7 +81,7 @@ func (m msgServer) InsertBTCSpvProof(ctx context.Context, req *types.MsgInsertBT submissionData := rawSubmission.GetSubmissionData(epochNum, txsInfo) // Everything is fine, save new checkpoint and update Epoch data - err = m.k.addEpochSubmission( + err = ms.k.addEpochSubmission( sdkCtx, epochNum, submissionKey, @@ -100,6 +100,9 @@ func (ms msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdatePara if ms.k.authority != req.Authority { return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.k.authority, req.Authority) } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } ctx := sdk.UnwrapSDKContext(goCtx) if err := ms.k.SetParams(ctx, req.Params); err != nil { diff --git a/x/btccheckpoint/keeper/msg_server_test.go b/x/btccheckpoint/keeper/msg_server_test.go index b621d444b..651eedb7d 100644 --- a/x/btccheckpoint/keeper/msg_server_test.go +++ b/x/btccheckpoint/keeper/msg_server_test.go @@ -8,8 +8,6 @@ import ( "testing" "time" - "github.com/babylonchain/babylon/testutil/datagen" - dg "github.com/babylonchain/babylon/testutil/datagen" keepertest "github.com/babylonchain/babylon/testutil/keeper" bbn "github.com/babylonchain/babylon/types" @@ -25,6 +23,7 @@ type TestKeepers struct { Ctx context.Context BTCLightClient *btcctypes.MockBTCLightClientKeeper Checkpointing *btcctypes.MockCheckpointingKeeper + Incentive *btcctypes.MockIncentiveKeeper BTCCheckpoint *bkeeper.Keeper MsgSrv btcctypes.MsgServer } @@ -41,27 +40,23 @@ func b2Hash(m *btcctypes.MsgInsertBTCSpvProof) *bbn.BTCHeaderHashBytes { return m.Proofs[1].ConfirmingBtcHeader.Hash() } -//nolint:unused -func b2TxIdx(m *btcctypes.MsgInsertBTCSpvProof) uint32 { - return m.Proofs[1].BtcTransactionIndex -} - func InitTestKeepers( t *testing.T, ) *TestKeepers { lc := btcctypes.NewMockBTCLightClientKeeper() - cc := btcctypes.NewMockCheckpointingKeeper() + ic := btcctypes.NewMockIncentiveKeeper() - k, ctx := keepertest.NewBTCCheckpointKeeper(t, lc, cc, chaincfg.SimNetParams.PowLimit) + k, ctx := keepertest.NewBTCCheckpointKeeper(t, lc, cc, ic, chaincfg.SimNetParams.PowLimit) srv := bkeeper.NewMsgServerImpl(*k) return &TestKeepers{ SdkCtx: ctx, - Ctx: sdk.WrapSDKContext(ctx), + Ctx: ctx, BTCLightClient: lc, Checkpointing: cc, + Incentive: ic, BTCCheckpoint: k, MsgSrv: srv, } @@ -97,8 +92,8 @@ func TestRejectDuplicatedSubmission(t *testing.T) { msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) // Now we will return depth enough for moving submission to be submitted - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(1)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(1)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(1)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), uint64(1)) _, err := tk.insertProofMsg(msg) @@ -135,48 +130,11 @@ func TestRejectUnknownToBtcLightClient(t *testing.T) { require.ErrorContainsf(t, err, btcctypes.ErrInvalidHeader.Error(), "Processing should return invalid header error") // even if one header is known, submission should still be considered invalid - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(1)) - - _, err = tk.insertProofMsg(msg) - - require.ErrorContainsf(t, err, btcctypes.ErrInvalidHeader.Error(), "Processing should return invalid header error") -} - -func TestRejectSubmissionsNotOnMainchain(t *testing.T) { - r := rand.New(rand.NewSource(time.Now().Unix())) - epoch := uint64(1) - raw, _ := dg.RandomRawCheckpointDataForEpoch(r, epoch) - - blck1 := dg.CreateBlock(r, 1, 7, 7, raw.FirstPart) - blck2 := dg.CreateBlock(r, 2, 14, 3, raw.SecondPart) - - tk := InitTestKeepers(t) - - msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) - - // both headers on fork, fail - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(-1)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(-1)) - - _, err := tk.insertProofMsg(msg) - - require.ErrorContainsf(t, err, btcctypes.ErrInvalidHeader.Error(), "Processing should return invalid header error") - - // one header on fork, one on main chain, fail - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(0)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(-1)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(1)) _, err = tk.insertProofMsg(msg) require.ErrorContainsf(t, err, btcctypes.ErrInvalidHeader.Error(), "Processing should return invalid header error") - - // two headers on main chain, success - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(0)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(0)) - - _, err = tk.insertProofMsg(msg) - - require.NoError(t, err, "Processing msg should succeed") } func TestSubmitValidNewCheckpoint(t *testing.T) { @@ -192,8 +150,8 @@ func TestSubmitValidNewCheckpoint(t *testing.T) { msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) // Now we will return depth enough for moving submission to be submitted - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(1)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(1)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(1)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), uint64(1)) _, err := tk.insertProofMsg(msg) @@ -201,7 +159,7 @@ func TestSubmitValidNewCheckpoint(t *testing.T) { ed := tk.GetEpochData(epoch) - if len(ed.Key) == 0 { + if len(ed.Keys) == 0 { t.Errorf("There should be at least one key in epoch %d", epoch) } @@ -209,7 +167,7 @@ func TestSubmitValidNewCheckpoint(t *testing.T) { t.Errorf("Epoch should be in submitted state after processing message") } - submissionKey := ed.Key[0] + submissionKey := ed.Keys[0] submissionData := tk.getSubmissionData(*submissionKey) @@ -240,7 +198,7 @@ func TestSubmitValidNewCheckpoint(t *testing.T) { // TODO Add custom equal fo submission key and transaction key to check // it is expected key - if len(ed1.Key) == 0 { + if len(ed1.Keys) == 0 { t.Errorf("Unexpected missing unconfirmed submissions") } } @@ -258,8 +216,8 @@ func TestRejectSubmissionWithoutSubmissionsForPreviousEpoch(t *testing.T) { msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) // Now we will return depth enough for moving submission to be submitted - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(0)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(1)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(0)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), uint64(1)) _, err := tk.insertProofMsg(msg) @@ -283,8 +241,8 @@ func TestRejectSubmissionWithoutAncestorsOnMainchainInPreviousEpoch(t *testing.T msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{epoch1Block1, epoch1Block2}) // Now we will return depth enough for moving submission to be submitted - tk.BTCLightClient.SetDepth(epoch1Block1.HeaderBytes.Hash(), int64(5)) - tk.BTCLightClient.SetDepth(epoch1Block2.HeaderBytes.Hash(), int64(4)) + tk.BTCLightClient.SetDepth(epoch1Block1.HeaderBytes.Hash(), uint64(5)) + tk.BTCLightClient.SetDepth(epoch1Block2.HeaderBytes.Hash(), uint64(4)) _, err := tk.insertProofMsg(msg) @@ -297,8 +255,8 @@ func TestRejectSubmissionWithoutAncestorsOnMainchainInPreviousEpoch(t *testing.T msg2 := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{epoch2Block1, epoch2Block2}) // Both headers are deeper than epoch 1 submission, fail - tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), int64(7)) - tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), int64(6)) + tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), uint64(7)) + tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), uint64(6)) _, err = tk.insertProofMsg(msg2) @@ -310,8 +268,8 @@ func TestRejectSubmissionWithoutAncestorsOnMainchainInPreviousEpoch(t *testing.T ) // one header deeper than headers of previous epoch, one fresher, fail - tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), int64(7)) - tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), int64(3)) + tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), uint64(7)) + tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), uint64(3)) _, err = tk.insertProofMsg(msg2) @@ -323,8 +281,8 @@ func TestRejectSubmissionWithoutAncestorsOnMainchainInPreviousEpoch(t *testing.T ) // one header on the same depth as previous epoch, one fresher, fail - tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), int64(4)) - tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), int64(3)) + tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), uint64(4)) + tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), uint64(3)) _, err = tk.insertProofMsg(msg2) @@ -336,8 +294,8 @@ func TestRejectSubmissionWithoutAncestorsOnMainchainInPreviousEpoch(t *testing.T ) // Both Headers fresher that previous epoch, succeed - tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), int64(3)) - tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), int64(2)) + tk.BTCLightClient.SetDepth(epoch2Block1.HeaderBytes.Hash(), uint64(3)) + tk.BTCLightClient.SetDepth(epoch2Block2.HeaderBytes.Hash(), uint64(2)) _, err = tk.insertProofMsg(msg2) @@ -350,38 +308,38 @@ func TestClearChildEpochsWhenNoParenNotOnMainChain(t *testing.T) { tk := InitTestKeepers(t) msg1 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg1), int64(5)) - tk.BTCLightClient.SetDepth(b2Hash(msg1), int64(4)) + tk.BTCLightClient.SetDepth(b1Hash(msg1), uint64(5)) + tk.BTCLightClient.SetDepth(b2Hash(msg1), uint64(4)) _, err := tk.insertProofMsg(msg1) require.NoError(t, err, "failed to insert submission for epoch 1") msg1a := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg1a), int64(4)) - tk.BTCLightClient.SetDepth(b2Hash(msg1a), int64(5)) + tk.BTCLightClient.SetDepth(b1Hash(msg1a), uint64(4)) + tk.BTCLightClient.SetDepth(b2Hash(msg1a), uint64(5)) _, err = tk.insertProofMsg(msg1a) require.NoError(t, err, "failed to insert submission for epoch 1") msg2 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 2) - tk.BTCLightClient.SetDepth(b1Hash(msg2), int64(3)) - tk.BTCLightClient.SetDepth(b2Hash(msg2), int64(2)) + tk.BTCLightClient.SetDepth(b1Hash(msg2), uint64(3)) + tk.BTCLightClient.SetDepth(b2Hash(msg2), uint64(2)) _, err = tk.insertProofMsg(msg2) require.NoError(t, err, "failed to insert submission for epoch 2") msg2a := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 2) - tk.BTCLightClient.SetDepth(b1Hash(msg2a), int64(3)) - tk.BTCLightClient.SetDepth(b2Hash(msg2a), int64(2)) + tk.BTCLightClient.SetDepth(b1Hash(msg2a), uint64(3)) + tk.BTCLightClient.SetDepth(b2Hash(msg2a), uint64(2)) _, err = tk.insertProofMsg(msg2a) require.NoError(t, err, "failed to insert submission for epoch 2") msg3 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 3) - tk.BTCLightClient.SetDepth(b1Hash(msg3), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg3), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg3), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg3), uint64(0)) _, err = tk.insertProofMsg(msg3) require.NoError(t, err, "failed to insert submission for epoch 3") msg3a := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 3) - tk.BTCLightClient.SetDepth(b1Hash(msg3a), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg3a), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg3a), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg3a), uint64(0)) _, err = tk.insertProofMsg(msg3a) require.NoError(t, err, "failed to insert submission for epoch 3") @@ -389,14 +347,14 @@ func TestClearChildEpochsWhenNoParenNotOnMainChain(t *testing.T) { // all 3 epoch must have two submissions ed := tk.GetEpochData(uint64(i)) require.NotNil(t, ed) - require.Len(t, ed.Key, 2) + require.Len(t, ed.Keys, 2) require.EqualValues(t, ed.Status, btcctypes.Submitted) } // Due to reorg one submission from epoch 1 lands on fork, which means it is no // longer vaiable. It should be pruned. Other subbmissions should be left // intact - tk.BTCLightClient.SetDepth(b2Hash(msg1), -1) + tk.BTCLightClient.DeleteHeader(b2Hash(msg1)) tk.onTipChange() @@ -406,17 +364,17 @@ func TestClearChildEpochsWhenNoParenNotOnMainChain(t *testing.T) { if i == 1 { // forked submission got pruned - require.Len(t, ed.Key, 1) + require.Len(t, ed.Keys, 1) } else { // other submissions still have parent so they are left intact - require.Len(t, ed.Key, 2) + require.Len(t, ed.Keys, 2) } require.EqualValues(t, ed.Status, btcctypes.Submitted) } // second submission from epoch 1 got orphaned. Clear it, and submissions from // child epochs - tk.BTCLightClient.SetDepth(b2Hash(msg1a), -1) + tk.BTCLightClient.DeleteHeader(b2Hash(msg1a)) tk.onTipChange() @@ -424,7 +382,7 @@ func TestClearChildEpochsWhenNoParenNotOnMainChain(t *testing.T) { // all 3 epoch must have two submissions ed := tk.GetEpochData(uint64(i)) require.NotNil(t, ed) - require.Len(t, ed.Key, 0) + require.Len(t, ed.Keys, 0) require.EqualValues(t, ed.Status, btcctypes.Submitted) } } @@ -436,43 +394,43 @@ func TestLeaveOnlyBestSubmissionWhenEpochFinalized(t *testing.T) { wDeep := defaultParams.CheckpointFinalizationTimeout msg1 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg1), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg1), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg1), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg1), uint64(0)) _, err := tk.insertProofMsg(msg1) require.NoError(t, err, "failed to insert submission") msg2 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg2), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg2), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg2), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg2), uint64(0)) _, err = tk.insertProofMsg(msg2) require.NoError(t, err, "failed to insert submission") msg3 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg3), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg3), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg3), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg3), uint64(0)) _, err = tk.insertProofMsg(msg3) require.NoError(t, err, "failed to insert submission") ed := tk.GetEpochData(uint64(1)) require.NotNil(t, ed) - require.Len(t, ed.Key, 3) + require.Len(t, ed.Keys, 3) // deepest submission is submission in msg3 - tk.BTCLightClient.SetDepth(b1Hash(msg1), int64(wDeep)) - tk.BTCLightClient.SetDepth(b2Hash(msg1), int64(wDeep+1)) - tk.BTCLightClient.SetDepth(b1Hash(msg2), int64(wDeep+2)) - tk.BTCLightClient.SetDepth(b2Hash(msg2), int64(wDeep+3)) - tk.BTCLightClient.SetDepth(b1Hash(msg3), int64(wDeep+4)) - tk.BTCLightClient.SetDepth(b2Hash(msg3), int64(wDeep+5)) + tk.BTCLightClient.SetDepth(b1Hash(msg1), wDeep) + tk.BTCLightClient.SetDepth(b2Hash(msg1), wDeep+1) + tk.BTCLightClient.SetDepth(b1Hash(msg2), wDeep+2) + tk.BTCLightClient.SetDepth(b2Hash(msg2), wDeep+3) + tk.BTCLightClient.SetDepth(b1Hash(msg3), wDeep+4) + tk.BTCLightClient.SetDepth(b2Hash(msg3), wDeep+5) tk.onTipChange() ed = tk.GetEpochData(uint64(1)) require.NotNil(t, ed) - require.Len(t, ed.Key, 1) + require.Len(t, ed.Keys, 1) require.Equal(t, ed.Status, btcctypes.Finalized) - finalSubKey := ed.Key[0] + finalSubKey := ed.Keys[0] require.Equal(t, finalSubKey.Key[0].Hash, b1Hash(msg3)) require.Equal(t, finalSubKey.Key[1].Hash, b2Hash(msg3)) @@ -485,36 +443,36 @@ func TestTxIdxShouldBreakTies(t *testing.T) { wDeep := defaultParams.CheckpointFinalizationTimeout msg1 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg1), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg1), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg1), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg1), uint64(0)) _, err := tk.insertProofMsg(msg1) require.NoError(t, err, "failed to insert submission") msg2 := dg.GenerateMessageWithRandomSubmitterForEpoch(r, 1) - tk.BTCLightClient.SetDepth(b1Hash(msg2), int64(1)) - tk.BTCLightClient.SetDepth(b2Hash(msg2), int64(0)) + tk.BTCLightClient.SetDepth(b1Hash(msg2), uint64(1)) + tk.BTCLightClient.SetDepth(b2Hash(msg2), uint64(0)) _, err = tk.insertProofMsg(msg2) require.NoError(t, err, "failed to insert submission") ed := tk.GetEpochData(uint64(1)) require.NotNil(t, ed) - require.Len(t, ed.Key, 2) + require.Len(t, ed.Keys, 2) // Both submissions have the same depth the most fresh block i.e // it is the same block // When finalizing the one with lower TxIx should be treated as better - tk.BTCLightClient.SetDepth(b1Hash(msg1), int64(wDeep)) - tk.BTCLightClient.SetDepth(b2Hash(msg1), int64(wDeep+1)) - tk.BTCLightClient.SetDepth(b1Hash(msg2), int64(wDeep)) - tk.BTCLightClient.SetDepth(b2Hash(msg2), int64(wDeep+3)) + tk.BTCLightClient.SetDepth(b1Hash(msg1), wDeep) + tk.BTCLightClient.SetDepth(b2Hash(msg1), wDeep+1) + tk.BTCLightClient.SetDepth(b1Hash(msg2), wDeep) + tk.BTCLightClient.SetDepth(b2Hash(msg2), wDeep+3) tk.onTipChange() ed = tk.GetEpochData(uint64(1)) require.NotNil(t, ed) - require.Len(t, ed.Key, 1) + require.Len(t, ed.Keys, 1) require.Equal(t, ed.Status, btcctypes.Finalized) - finalSubKey := ed.Key[0] + finalSubKey := ed.Keys[0] // There is small chance that we can draw the same transactions indexes, which // cannot happend in real life i.e in real life if block has the same depth @@ -543,8 +501,8 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) // Now we will return depth enough for moving submission to confirmed - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(1)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(1)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(1)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), uint64(1)) _, err := tk.insertProofMsg(msg) @@ -555,7 +513,7 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { // TODO customs Equality for submission keys ed := tk.GetEpochData(epoch) - if len(ed.Key) != 1 { + if len(ed.Keys) != 1 { t.Errorf("Unexpected missing submissions") } @@ -564,8 +522,8 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { } // Now we will return depth enough for moving submission to confirmed - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(kDeep)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(kDeep)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), kDeep) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), kDeep) // fire tip change callback tk.onTipChange() @@ -573,7 +531,7 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { // we are looking for ed = tk.GetEpochData(epoch) - if len(ed.Key) != 1 { + if len(ed.Keys) != 1 { t.Errorf("Unexpected missing submission") } @@ -581,8 +539,8 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { t.Errorf("Epoch should be in submitted stated") } - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(wDeep)) - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(wDeep)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), wDeep) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), wDeep) tk.onTipChange() @@ -594,7 +552,7 @@ func TestStateTransitionOfValidSubmission(t *testing.T) { } func FuzzConfirmAndDinalizeManyEpochs(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 20) + dg.AddRandomSeedsToFuzzer(f, 20) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) @@ -629,9 +587,9 @@ func FuzzConfirmAndDinalizeManyEpochs(f *testing.F) { msg := dg.GenerateMessageWithRandomSubmitter([]*dg.BlockCreationResult{blck1, blck2}) if epoch <= uint64(numFinalizedEpochs) { - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(finalizationDepth)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), uint64(finalizationDepth)) finalizationDepth = finalizationDepth - 1 - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(finalizationDepth)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), uint64(finalizationDepth)) // first submission is always deepest one, and second block is the most recent one if j == 1 { @@ -639,21 +597,21 @@ func FuzzConfirmAndDinalizeManyEpochs(f *testing.F) { } finalizationDepth = finalizationDepth - 1 } else if epoch <= uint64(numFinalizedEpochs+numConfirmedEpochs) { - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(confirmationDepth)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), confirmationDepth) confirmationDepth = confirmationDepth - 1 - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(confirmationDepth)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), confirmationDepth) // first submission is always deepest one, and second block is the most recent one if j == 1 { - bestSumbissionInfos[epoch] = uint64(confirmationDepth) + bestSumbissionInfos[epoch] = confirmationDepth } confirmationDepth = confirmationDepth - 1 } else { - tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), int64(sumbissionDepth)) + tk.BTCLightClient.SetDepth(blck1.HeaderBytes.Hash(), sumbissionDepth) sumbissionDepth = sumbissionDepth - 1 - tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), int64(sumbissionDepth)) + tk.BTCLightClient.SetDepth(blck2.HeaderBytes.Hash(), sumbissionDepth) // first submission is always deepest one, and second block is the most recent one if j == 1 { - bestSumbissionInfos[epoch] = uint64(sumbissionDepth) + bestSumbissionInfos[epoch] = sumbissionDepth } sumbissionDepth = sumbissionDepth - 1 } @@ -682,7 +640,7 @@ func FuzzConfirmAndDinalizeManyEpochs(f *testing.F) { if epoch <= uint64(numFinalizedEpochs) { require.Equal(t, ed.Status, btcctypes.Finalized) // finalized epochs should have only best submission - require.Equal(t, len(ed.Key), 1) + require.Equal(t, len(ed.Keys), 1) } else if epoch <= uint64(numFinalizedEpochs+numConfirmedEpochs) { require.Equal(t, ed.Status, btcctypes.Confirmed) } else { diff --git a/x/btccheckpoint/keeper/params.go b/x/btccheckpoint/keeper/params.go index c69f94aa2..a4333facb 100644 --- a/x/btccheckpoint/keeper/params.go +++ b/x/btccheckpoint/keeper/params.go @@ -1,25 +1,27 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/btccheckpoint/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // SetParams sets the x/btccheckpoint module parameters. -func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { if err := p.Validate(); err != nil { return err } - store := ctx.KVStore(k.storeKey) + store := k.storeService.OpenKVStore(ctx) bz := k.cdc.MustMarshal(&p) - store.Set(types.ParamsKey, bz) - return nil + return store.Set(types.ParamsKey, bz) } // GetParams returns the current x/btccheckpoint module parameters. -func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ParamsKey) +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } if bz == nil { return p } diff --git a/x/btccheckpoint/keeper/params_test.go b/x/btccheckpoint/keeper/params_test.go index d690964a2..444e46eb9 100644 --- a/x/btccheckpoint/keeper/params_test.go +++ b/x/btccheckpoint/keeper/params_test.go @@ -9,7 +9,7 @@ import ( ) func TestGetParams(t *testing.T) { - k, ctx := testkeeper.NewBTCCheckpointKeeper(t, nil, nil, nil) + k, ctx := testkeeper.NewBTCCheckpointKeeper(t, nil, nil, nil, nil) params := types.DefaultParams() diff --git a/x/btccheckpoint/keeper/submissions.go b/x/btccheckpoint/keeper/submissions.go new file mode 100644 index 000000000..9bf5d400c --- /dev/null +++ b/x/btccheckpoint/keeper/submissions.go @@ -0,0 +1,259 @@ +package keeper + +import ( + "context" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "math" + + "cosmossdk.io/store/prefix" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btccheckpoint/types" +) + +func (k Keeper) HasSubmission(ctx context.Context, sk types.SubmissionKey) bool { + store := k.storeService.OpenKVStore(ctx) + kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) + has, err := store.Has(kBytes) + if err != nil { + panic(err) + } + return has +} + +// GetBestSubmission gets the status and the best submission of a given finalized epoch +func (k Keeper) GetBestSubmission(ctx context.Context, epochNumber uint64) (types.BtcStatus, *types.SubmissionKey, error) { + // find the btc checkpoint tx index of this epoch + ed := k.GetEpochData(ctx, epochNumber) + if ed == nil { + return 0, nil, types.ErrNoCheckpointsForPreviousEpoch + } + if ed.Status != types.Finalized { + return 0, nil, fmt.Errorf("epoch %d has not been finalized yet", epochNumber) + } + if len(ed.Keys) == 0 { + return 0, nil, types.ErrNoCheckpointsForPreviousEpoch + } + bestSubmissionKey := ed.Keys[0] // index of checkpoint tx on BTC + + return ed.Status, bestSubmissionKey, nil +} + +// addEpochSubmission save given submission key and data to database and takes +// car of updating any necessary indexes. +// Provided submmission should be known to btclightclient and all of its blocks +// should be on btc main chaing as viewed by btclightclient +func (k Keeper) addEpochSubmission( + ctx context.Context, + epochNum uint64, + sk types.SubmissionKey, + sd types.SubmissionData, +) error { + + ed := k.GetEpochData(ctx, epochNum) + + // TODO: SaveEpochData and SaveSubmission should be done in one transaction. + // Not sure cosmos-sdk has facialities to do it. + // Otherwise it is possible to end up with node which updated submission list + // but did not save submission itself. + + // if ed is nil, it means it is our first submission for this epoch + if ed == nil { + // we do not have any data saved yet + newEd := types.NewEmptyEpochData() + ed = &newEd + } + + if ed.Status == types.Finalized { + // we already finlized given epoch so we do not need any more submissions + // TODO We should probably compare new submmission with the exisiting submission + // which finalized the epoch. As it means we finalized epoch with not the best + // submission possible + return types.ErrEpochAlreadyFinalized + } + + if len(ed.Keys) == 0 { + // it is first epoch submission inform checkpointing module about this fact + k.checkpointingKeeper.SetCheckpointSubmitted(ctx, epochNum) + } + + ed.AppendKey(sk) + k.saveEpochData(ctx, epochNum, ed) + k.saveSubmission(ctx, sk, sd) + return nil +} + +func (k Keeper) saveSubmission(ctx context.Context, sk types.SubmissionKey, sd types.SubmissionData) { + store := k.storeService.OpenKVStore(ctx) + kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) + sBytes := k.cdc.MustMarshal(&sd) + if err := store.Set(kBytes, sBytes); err != nil { + panic(err) + } +} + +func (k Keeper) deleteSubmission(ctx context.Context, sk types.SubmissionKey) { + store := k.storeService.OpenKVStore(ctx) + kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) + if err := store.Delete(kBytes); err != nil { + panic(err) + } +} + +// GetSubmissionData returns submission data for a given key or nil if there is no data +// under the given key +func (k Keeper) GetSubmissionData(ctx context.Context, sk types.SubmissionKey) *types.SubmissionData { + store := k.storeService.OpenKVStore(ctx) + kBytes := types.PrefixedSubmisionKey(k.cdc, &sk) + sdBytes, err := store.Get(kBytes) + if err != nil { + panic(err) + } + + if len(sdBytes) == 0 { + return nil + } + + var sd types.SubmissionData + k.cdc.MustUnmarshal(sdBytes, &sd) + return &sd +} + +func (k Keeper) checkSubmissionStatus(ctx context.Context, info *types.SubmissionBtcInfo) types.BtcStatus { + subDepth := info.SubmissionDepth() + if subDepth >= k.GetParams(ctx).CheckpointFinalizationTimeout { + return types.Finalized + } else if subDepth >= k.GetParams(ctx).BtcConfirmationDepth { + return types.Confirmed + } else { + return types.Submitted + } +} + +func (k Keeper) GetSubmissionBtcInfo(ctx context.Context, sk types.SubmissionKey) (*types.SubmissionBtcInfo, error) { + + var youngestBlockDepth uint64 = math.MaxUint64 + var youngestBlockHash *bbn.BTCHeaderHashBytes + + var lowestIndexInMostFreshBlock uint32 = math.MaxUint32 + + var oldestBlockDepth = uint64(0) + + for _, tk := range sk.Key { + currentBlockDepth, err := k.headerDepth(ctx, tk.Hash) + + if err != nil { + return nil, err + } + + if currentBlockDepth < youngestBlockDepth { + youngestBlockDepth = currentBlockDepth + lowestIndexInMostFreshBlock = tk.Index + youngestBlockHash = tk.Hash + } + + // This case happens when we have two submissions in the same block. + if currentBlockDepth == youngestBlockDepth && tk.Index < lowestIndexInMostFreshBlock { + // This is something which needs a bit more careful thinking as it is used + // to determine which submission is better. + // Currently if two submissions of one checkpoint are in the same block, + // we pick tx with lower index as the point at which checkpoint happened. + // This is in line with the logic that if two submission are in the same block, + // they are esentially happening at the same time, so it does not really matter + // which index pick, and for possibble tie breaks it is better to pick lower one. + // This means in case when we have: + // Checkpoint submission `x` for epoch 5, both tx in same block at height 100, with indexes 1 and 10 + // and + // Checkpoint submission `y` for epoch 5, both tx in same block at height 100, with indexes 3 and 9 + // we will chose submission `x` as the better one. + // This good enough solution, but it is not perfect and leads to some edge cases like: + // Checkpoint submission `x` for epoch 5, one tx in block 99 with index 1, and second tx in block 100 with index 4 + // and + // Checkpoint submission `y` for epoch 5, both tx in same block at height 100, with indexes 3 and 9 + // In this case submission `y` will be better as it `earliest` tx in most fresh block is first. But at first glance + // submission `x` seems better. + lowestIndexInMostFreshBlock = tk.Index + } + + if currentBlockDepth > oldestBlockDepth { + oldestBlockDepth = currentBlockDepth + } + } + + return &types.SubmissionBtcInfo{ + SubmissionKey: sk, + OldestBlockDepth: oldestBlockDepth, + YoungestBlockDepth: youngestBlockDepth, + YoungestBlockHash: *youngestBlockHash, + YoungestBlockLowestTxIdx: lowestIndexInMostFreshBlock, + }, nil +} + +func (k Keeper) GetEpochBestSubmissionBtcInfo(ctx context.Context, ed *types.EpochData) *types.SubmissionBtcInfo { + // there are no submissions for this epoch, so transitivly there is no best submission + if ed == nil || len(ed.Keys) == 0 { + return nil + } + + // There is only one submission for this epoch: + // - either epoch is already finalized, and we already chose the best submission + // - or we only received one submission for this epoch + // Either way, we do not need to decide which submission is the best one. + if len(ed.Keys) == 1 { + sk := *ed.Keys[0] + btcInfo, err := k.GetSubmissionBtcInfo(ctx, sk) + + if err != nil { + k.Logger(sdk.UnwrapSDKContext(ctx)).Debug("Previously stored submission is not valid anymore. Submission key: %+v", sk) + } + + // we only log error, as the only error which we can receive here is that submission + // is no longer on btc canonical chain, which essentially means that there is no valid submission + return btcInfo + } + + // We have more that one valid submission. We need to chose the best one. + epochSummary := k.getEpochChanges(ctx, nil, ed) + + return epochSummary.EpochBestSubmission +} + +// GetEpochData returns epoch data for given epoch, if there is not epoch data yet returns nil +func (k Keeper) GetEpochData(ctx context.Context, e uint64) *types.EpochData { + store := k.storeService.OpenKVStore(ctx) + bytes, err := store.Get(types.GetEpochIndexKey(e)) + if err != nil { + panic(err) + } + + // note: Cannot check len(bytes) == 0, as empty bytes encoding of types.EpochData + // is epoch data with Status == Submitted and no valid submissions + if bytes == nil { + return nil + } + + ed := &types.EpochData{} + k.cdc.MustUnmarshal(bytes, ed) + return ed +} + +func (k Keeper) saveEpochData(ctx context.Context, e uint64, ed *types.EpochData) { + store := k.storeService.OpenKVStore(ctx) + ek := types.GetEpochIndexKey(e) + eb := k.cdc.MustMarshal(ed) + if err := store.Set(ek, eb); err != nil { + panic(err) + } +} + +func (k Keeper) clearEpochData( + ctx context.Context, + epoch []byte, + epochDataStore prefix.Store, + currentEpoch *types.EpochData) { + for _, sk := range currentEpoch.Keys { + k.deleteSubmission(ctx, *sk) + } + currentEpoch.Keys = []*types.SubmissionKey{} + epochDataStore.Set(epoch, k.cdc.MustMarshal(currentEpoch)) +} diff --git a/x/btccheckpoint/module.go b/x/btccheckpoint/module.go index 879902e9a..40db44228 100644 --- a/x/btccheckpoint/module.go +++ b/x/btccheckpoint/module.go @@ -2,6 +2,7 @@ package btccheckpoint import ( "context" + "cosmossdk.io/core/appmodule" "encoding/json" "fmt" @@ -22,8 +23,10 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // ---------------------------------------------------------------------------- @@ -77,7 +80,10 @@ func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Rout // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck // generally we don't handle errors in these registration functions + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } } // GetTxCmd returns the capability module's root tx command. @@ -98,23 +104,16 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { type AppModule struct { AppModuleBasic - keeper keeper.Keeper - accountKeeper types.AccountKeeper - bankKeeper types.BankKeeper - // TODO: add dependencies to staking, slashing and evidence + keeper keeper.Keeper } func NewAppModule( cdc codec.Codec, keeper keeper.Keeper, - accountKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, ) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), keeper: keeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, } } @@ -138,14 +137,12 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the capability module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. @@ -158,11 +155,21 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(_ context.Context) error { + return nil +} // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. -func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { - EndBlocker(ctx, am.keeper, req) - return []abci.ValidatorUpdate{} +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { + EndBlocker(ctx, am.keeper) + return []abci.ValidatorUpdate{}, nil +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker } diff --git a/x/btccheckpoint/module_simulation.go b/x/btccheckpoint/module_simulation.go deleted file mode 100644 index 804960957..000000000 --- a/x/btccheckpoint/module_simulation.go +++ /dev/null @@ -1,47 +0,0 @@ -package btccheckpoint - -import ( - simappparams "github.com/babylonchain/babylon/app/params" - btccheckpointsimulation "github.com/babylonchain/babylon/x/btccheckpoint/simulation" - "github.com/babylonchain/babylon/x/btccheckpoint/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = btccheckpointsimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - accs := make([]string, len(simState.Accounts)) - for i, acc := range simState.Accounts { - accs[i] = acc.Address.String() - } - btccheckpointGenesis := types.GenesisState{ - Params: types.DefaultParams(), - } - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&btccheckpointGenesis) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - operations := make([]simtypes.WeightedOperation, 0) - - return operations -} diff --git a/x/btccheckpoint/simulation/simap.go b/x/btccheckpoint/simulation/simap.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/btccheckpoint/simulation/simap.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/btccheckpoint/types/btccheckpoint.pb.go b/x/btccheckpoint/types/btccheckpoint.pb.go index 2c5545b37..23f50d154 100644 --- a/x/btccheckpoint/types/btccheckpoint.pb.go +++ b/x/btccheckpoint/types/btccheckpoint.pb.go @@ -245,7 +245,7 @@ func (m *SubmissionKey) GetKey() []*TransactionKey { return nil } -// TransactionInfo is the info of a tx that contains Babylon checkpoint, +// TransactionInfo is the info of a tx on Bitcoin, // including // - the position of the tx on BTC blockchain // - the full tx content @@ -394,10 +394,10 @@ func (m *SubmissionData) GetEpoch() uint64 { // TODO: Add btc blockheight at epooch end, when adding hadnling of epoching // callbacks type EpochData struct { - // List of all received checkpoints during this epoch, sorted by order of - // submission. - Key []*SubmissionKey `protobuf:"bytes,1,rep,name=key,proto3" json:"key,omitempty"` - // Current btc status of the epoch + // keys is the list of all received checkpoints during this epoch, sorted by + // order of submission. + Keys []*SubmissionKey `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + // status is the current btc status of the epoch Status BtcStatus `protobuf:"varint,2,opt,name=status,proto3,enum=babylon.btccheckpoint.v1.BtcStatus" json:"status,omitempty"` } @@ -434,9 +434,9 @@ func (m *EpochData) XXX_DiscardUnknown() { var xxx_messageInfo_EpochData proto.InternalMessageInfo -func (m *EpochData) GetKey() []*SubmissionKey { +func (m *EpochData) GetKeys() []*SubmissionKey { if m != nil { - return m.Key + return m.Keys } return nil } @@ -601,58 +601,58 @@ func init() { } var fileDescriptor_e096cac78d49b0a6 = []byte{ - // 809 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4b, 0x6f, 0xea, 0x46, - 0x14, 0x66, 0x80, 0xdc, 0x5e, 0x06, 0xc2, 0x4d, 0x87, 0xdc, 0xca, 0x42, 0x91, 0x2f, 0xd7, 0x95, - 0x1a, 0x52, 0xb5, 0xa0, 0xa4, 0xad, 0x94, 0x3e, 0x36, 0x31, 0x0f, 0x81, 0x92, 0x40, 0x64, 0x9c, - 0x2e, 0xb2, 0xa8, 0x65, 0x9b, 0x01, 0x8f, 0x00, 0x0f, 0xf2, 0x0c, 0x08, 0xba, 0x6a, 0x55, 0x55, - 0xaa, 0xba, 0xaa, 0xba, 0xef, 0xaa, 0x7f, 0xa6, 0x8b, 0x2e, 0xb2, 0xac, 0xb2, 0x88, 0xaa, 0xe4, - 0x1f, 0x74, 0xdb, 0x4d, 0xe5, 0xb1, 0xc3, 0x2b, 0xa1, 0x2d, 0xd2, 0xdd, 0x71, 0xce, 0x7c, 0xe7, - 0xf1, 0x7d, 0xe7, 0x1c, 0x0c, 0x3f, 0xb0, 0x4c, 0x6b, 0xda, 0xa7, 0x6e, 0xd1, 0xe2, 0xb6, 0xed, - 0x60, 0xbb, 0x37, 0xa4, 0xc4, 0xe5, 0xc5, 0xf1, 0xe1, 0xb2, 0xa3, 0x30, 0xf4, 0x28, 0xa7, 0x48, - 0x0a, 0xd1, 0x85, 0xe5, 0xc7, 0xf1, 0x61, 0x76, 0xb7, 0x4b, 0xbb, 0x54, 0x80, 0x8a, 0xfe, 0xaf, - 0x00, 0xaf, 0xfc, 0x0d, 0x60, 0x52, 0xd5, 0x4b, 0xad, 0xe1, 0xf8, 0xc2, 0xa3, 0xb4, 0x83, 0xf6, - 0xe1, 0x0b, 0x8b, 0xdb, 0x06, 0xf7, 0x4c, 0x97, 0x99, 0x36, 0x27, 0xd4, 0x95, 0x40, 0x0e, 0xe4, - 0x53, 0x5a, 0xda, 0xe2, 0xb6, 0x3e, 0xf7, 0xa2, 0x23, 0xf8, 0x72, 0x05, 0x68, 0x10, 0xb7, 0x8d, - 0x27, 0x52, 0x34, 0x07, 0xf2, 0xdb, 0x5a, 0x66, 0x19, 0x5e, 0xf7, 0x9f, 0xd0, 0x6b, 0x98, 0x1a, - 0x60, 0xaf, 0xd7, 0xc7, 0x86, 0x4b, 0xdb, 0x98, 0x49, 0x31, 0x91, 0x39, 0x19, 0xf8, 0x1a, 0xbe, - 0x0b, 0xf5, 0xe1, 0x4b, 0x9b, 0xba, 0x1d, 0xe2, 0x0d, 0x88, 0xdb, 0x35, 0xfc, 0x0a, 0x0e, 0x36, - 0xdb, 0xd8, 0x93, 0xe2, 0x3e, 0x56, 0x3d, 0xbe, 0xb9, 0x7d, 0xf5, 0x71, 0x97, 0x70, 0x67, 0x64, - 0x15, 0x6c, 0x3a, 0x28, 0x86, 0x6c, 0x6d, 0xc7, 0x24, 0xee, 0x83, 0x51, 0xe4, 0xd3, 0x21, 0x66, - 0x05, 0x55, 0x2f, 0xd5, 0x44, 0xa8, 0x3a, 0xe5, 0x98, 0x69, 0x99, 0x79, 0x5a, 0x95, 0xdb, 0xc1, - 0x8b, 0x32, 0x81, 0xe9, 0x85, 0x26, 0x4f, 0xf1, 0x14, 0xed, 0xc2, 0xad, 0x80, 0x06, 0x10, 0x34, - 0x02, 0x03, 0x5d, 0xc0, 0xb8, 0x63, 0x32, 0x47, 0x70, 0x4b, 0xa9, 0x5f, 0xdc, 0xdc, 0xbe, 0x3a, - 0xde, 0xb0, 0x89, 0x9a, 0xc9, 0x9c, 0xa0, 0x11, 0x91, 0x49, 0x39, 0x85, 0xdb, 0xad, 0x91, 0x35, - 0x20, 0x8c, 0x85, 0x85, 0x3f, 0x83, 0xb1, 0x1e, 0x9e, 0x4a, 0x20, 0x17, 0xcb, 0x27, 0x8f, 0xf2, - 0x85, 0x75, 0x63, 0x2c, 0x2c, 0xf7, 0xab, 0xf9, 0x41, 0xca, 0xf7, 0x00, 0xbe, 0x58, 0x12, 0xbb, - 0x43, 0xe7, 0xf9, 0xc0, 0xc6, 0xf9, 0x50, 0x0e, 0x26, 0x17, 0x17, 0x20, 0x1a, 0x8c, 0x69, 0xc1, - 0xe5, 0xcb, 0x34, 0xf4, 0xf7, 0x25, 0x1c, 0x61, 0x60, 0x28, 0xbf, 0x03, 0x98, 0x9e, 0xb3, 0x2a, - 0x9b, 0xdc, 0x44, 0x5f, 0xc1, 0xcc, 0x98, 0x74, 0x49, 0xdf, 0x74, 0x39, 0x36, 0xcc, 0x76, 0xdb, - 0xc3, 0x8c, 0x61, 0x16, 0xb6, 0xf5, 0xe1, 0xfa, 0xb6, 0x4a, 0x33, 0xeb, 0xe4, 0x21, 0x48, 0x43, - 0xb3, 0x4c, 0x33, 0x1f, 0x2a, 0xc3, 0xe7, 0x7c, 0xc2, 0x0c, 0xe2, 0x76, 0xa8, 0x14, 0x15, 0xda, - 0x1d, 0xfc, 0x2f, 0xae, 0xbe, 0x46, 0xda, 0x5b, 0x7c, 0xc2, 0x84, 0x58, 0xbb, 0x70, 0x0b, 0x0f, - 0xa9, 0xed, 0x08, 0x3a, 0x71, 0x2d, 0x30, 0x94, 0xef, 0x00, 0x4c, 0x54, 0xfc, 0x5f, 0x82, 0xc9, - 0xa7, 0x8b, 0x03, 0xda, 0x5f, 0x5f, 0x64, 0x69, 0xac, 0x81, 0x9e, 0x9f, 0xc3, 0x67, 0x8c, 0x9b, - 0x7c, 0xc4, 0x84, 0x94, 0xe9, 0xa3, 0x77, 0xd7, 0x47, 0xab, 0xdc, 0x6e, 0x09, 0xa8, 0x16, 0x86, - 0x28, 0x4d, 0x98, 0x79, 0x42, 0x0c, 0xb4, 0x07, 0x13, 0xcc, 0xaf, 0xc4, 0x39, 0xf6, 0xc2, 0x13, - 0x9d, 0x3b, 0x50, 0x16, 0x3e, 0xf7, 0xf0, 0x90, 0x7a, 0xfe, 0x63, 0x30, 0xbe, 0x99, 0xad, 0xfc, - 0x15, 0x83, 0x6f, 0xab, 0x7a, 0x69, 0x9e, 0x54, 0x48, 0xf0, 0x1a, 0xa6, 0x04, 0x6b, 0xc3, 0x1d, - 0x0d, 0xac, 0x30, 0x65, 0x5c, 0x4b, 0x0a, 0x5f, 0x43, 0xb8, 0x50, 0x15, 0xe6, 0x2c, 0xcc, 0xb8, - 0xc1, 0x66, 0x0c, 0xc5, 0x81, 0x5a, 0x7d, 0x6a, 0xf7, 0x0c, 0x07, 0x93, 0xae, 0xc3, 0x45, 0xb1, - 0xb8, 0xb6, 0xe7, 0xe3, 0xe6, 0x42, 0xa8, 0xdc, 0x56, 0x7d, 0x50, 0x4d, 0x60, 0xd0, 0x37, 0x00, - 0xca, 0xff, 0x92, 0xc8, 0x3f, 0xb4, 0xd8, 0x1b, 0x38, 0xb4, 0xec, 0x9a, 0x26, 0x4c, 0xe6, 0xa0, - 0x1e, 0xdc, 0x5b, 0xed, 0x60, 0x61, 0xbd, 0x99, 0x14, 0xdf, 0x74, 0x95, 0x56, 0x8a, 0x2d, 0x3c, - 0x33, 0xf4, 0x2d, 0x80, 0xef, 0xad, 0x56, 0x7b, 0x74, 0x14, 0x46, 0x9f, 0x30, 0x2e, 0x6d, 0x89, - 0xba, 0x1b, 0xde, 0x85, 0xb2, 0x5c, 0xfb, 0xcb, 0x95, 0x2b, 0x39, 0x23, 0x8c, 0xbf, 0xff, 0x33, - 0x80, 0x89, 0xd9, 0x6e, 0xa1, 0x03, 0xf8, 0x4e, 0xe5, 0xa2, 0x59, 0xaa, 0x19, 0x2d, 0xfd, 0x44, - 0xbf, 0x6c, 0x19, 0xad, 0x4b, 0xf5, 0xbc, 0xae, 0xeb, 0x95, 0xf2, 0x4e, 0x24, 0xbb, 0xfd, 0xe3, - 0x2f, 0xb9, 0x44, 0x2b, 0xdc, 0xa4, 0xf6, 0x23, 0x68, 0xa9, 0xd9, 0xa8, 0xd6, 0xb5, 0xf3, 0x4a, - 0x79, 0x07, 0x04, 0xd0, 0x52, 0xf0, 0xbf, 0xfa, 0x04, 0xb4, 0x5a, 0x6f, 0x9c, 0x9c, 0xd5, 0xaf, - 0x2a, 0xe5, 0x9d, 0x68, 0x00, 0xad, 0x12, 0xd7, 0xec, 0x93, 0xaf, 0x71, 0x3b, 0x1b, 0xff, 0xe1, - 0x57, 0x39, 0xa2, 0x36, 0x7f, 0xbb, 0x93, 0xc1, 0xf5, 0x9d, 0x0c, 0xfe, 0xbc, 0x93, 0xc1, 0x4f, - 0xf7, 0x72, 0xe4, 0xfa, 0x5e, 0x8e, 0xfc, 0x71, 0x2f, 0x47, 0xae, 0x3e, 0xf9, 0xaf, 0xa9, 0x4f, - 0x56, 0x3e, 0x87, 0x62, 0x0b, 0xac, 0x67, 0xe2, 0xa3, 0xf6, 0xd1, 0x3f, 0x01, 0x00, 0x00, 0xff, - 0xff, 0xa5, 0xcd, 0xc5, 0xce, 0x34, 0x07, 0x00, 0x00, + // 814 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x8f, 0x22, 0x45, + 0x14, 0xa7, 0xa0, 0x67, 0x5d, 0x0a, 0x86, 0x1d, 0x8b, 0x59, 0xd3, 0x21, 0x93, 0x5e, 0xb6, 0x4d, + 0x5c, 0xd6, 0x28, 0x64, 0x47, 0x4d, 0x36, 0xae, 0x97, 0x69, 0x3e, 0x02, 0xd9, 0x5d, 0x98, 0x34, + 0xbd, 0x1e, 0xf6, 0x60, 0xa7, 0xbb, 0x29, 0xe8, 0x0a, 0xd0, 0x45, 0xba, 0x0a, 0x02, 0x9e, 0xf4, + 0x60, 0x62, 0x3c, 0x19, 0xef, 0x9e, 0xfc, 0x67, 0x3c, 0x78, 0xd8, 0xa3, 0xd9, 0xc3, 0xc4, 0xcc, + 0xfc, 0x07, 0x5e, 0xbd, 0x98, 0xaa, 0xee, 0xe1, 0x6b, 0x06, 0x95, 0xc4, 0x1b, 0xef, 0xd5, 0xef, + 0x7d, 0xfc, 0x7e, 0xef, 0x3d, 0x1a, 0x7e, 0xe4, 0x3a, 0xee, 0x62, 0x44, 0x83, 0x8a, 0xcb, 0x3d, + 0xcf, 0xc7, 0xde, 0x70, 0x42, 0x49, 0xc0, 0x2b, 0xb3, 0x27, 0x9b, 0x8e, 0xf2, 0x24, 0xa4, 0x9c, + 0x22, 0x35, 0x46, 0x97, 0x37, 0x1f, 0x67, 0x4f, 0x0a, 0xc7, 0x03, 0x3a, 0xa0, 0x12, 0x54, 0x11, + 0xbf, 0x22, 0xbc, 0xfe, 0x17, 0x80, 0x19, 0xc3, 0xaa, 0x76, 0x27, 0xb3, 0xf3, 0x90, 0xd2, 0x3e, + 0x7a, 0x04, 0xef, 0xb9, 0xdc, 0xb3, 0x79, 0xe8, 0x04, 0xcc, 0xf1, 0x38, 0xa1, 0x81, 0x0a, 0x8a, + 0xa0, 0x94, 0x35, 0x73, 0x2e, 0xf7, 0xac, 0x95, 0x17, 0x9d, 0xc2, 0xfb, 0x5b, 0x40, 0x9b, 0x04, + 0x3d, 0x3c, 0x57, 0x93, 0x45, 0x50, 0x3a, 0x34, 0xf3, 0x9b, 0xf0, 0x96, 0x78, 0x42, 0x0f, 0x61, + 0x76, 0x8c, 0xc3, 0xe1, 0x08, 0xdb, 0x01, 0xed, 0x61, 0xa6, 0xa6, 0x64, 0xe6, 0x4c, 0xe4, 0x6b, + 0x0b, 0x17, 0x1a, 0xc1, 0xfb, 0x1e, 0x0d, 0xfa, 0x24, 0x1c, 0x93, 0x60, 0x60, 0x8b, 0x0a, 0x3e, + 0x76, 0x7a, 0x38, 0x54, 0x15, 0x81, 0x35, 0x9e, 0xbe, 0xbd, 0x78, 0xf0, 0xe9, 0x80, 0x70, 0x7f, + 0xea, 0x96, 0x3d, 0x3a, 0xae, 0xc4, 0x6c, 0x3d, 0xdf, 0x21, 0xc1, 0xb5, 0x51, 0xe1, 0x8b, 0x09, + 0x66, 0x65, 0xc3, 0xaa, 0x36, 0x65, 0xa8, 0xb1, 0xe0, 0x98, 0x99, 0xf9, 0x55, 0x5a, 0x83, 0x7b, + 0xd1, 0x8b, 0x3e, 0x87, 0xb9, 0xb5, 0x26, 0x9f, 0xe3, 0x05, 0x3a, 0x86, 0x07, 0x11, 0x0d, 0x20, + 0x69, 0x44, 0x06, 0x3a, 0x87, 0x8a, 0xef, 0x30, 0x5f, 0x72, 0xcb, 0x1a, 0x5f, 0xbc, 0xbd, 0x78, + 0xf0, 0x74, 0xcf, 0x26, 0x9a, 0x0e, 0xf3, 0xa3, 0x46, 0x64, 0x26, 0xfd, 0x39, 0x3c, 0xec, 0x4e, + 0xdd, 0x31, 0x61, 0x2c, 0x2e, 0xfc, 0x39, 0x4c, 0x0d, 0xf1, 0x42, 0x05, 0xc5, 0x54, 0x29, 0x73, + 0x5a, 0x2a, 0xef, 0x1a, 0x63, 0x79, 0xb3, 0x5f, 0x53, 0x04, 0xe9, 0xdf, 0x01, 0x78, 0x6f, 0x43, + 0xec, 0x3e, 0x5d, 0xe5, 0x03, 0x7b, 0xe7, 0x43, 0x45, 0x98, 0x59, 0x5f, 0x80, 0x64, 0x34, 0xa6, + 0x35, 0x97, 0x90, 0x69, 0x22, 0xf6, 0x25, 0x1e, 0x61, 0x64, 0xe8, 0xbf, 0x01, 0x98, 0x5b, 0xb1, + 0xaa, 0x39, 0xdc, 0x41, 0x5f, 0xc1, 0xfc, 0x8c, 0x0c, 0xc8, 0xc8, 0x09, 0x38, 0xb6, 0x9d, 0x5e, + 0x2f, 0xc4, 0x8c, 0x61, 0x16, 0xb7, 0xf5, 0xf1, 0xee, 0xb6, 0xaa, 0x4b, 0xeb, 0xec, 0x3a, 0xc8, + 0x44, 0xcb, 0x4c, 0x4b, 0x1f, 0xaa, 0xc1, 0xbb, 0x7c, 0xce, 0x6c, 0x12, 0xf4, 0xa9, 0x9a, 0x94, + 0xda, 0x3d, 0xfe, 0x4f, 0x5c, 0x85, 0x46, 0xe6, 0x3b, 0x7c, 0xce, 0xa4, 0x58, 0xc7, 0xf0, 0x00, + 0x4f, 0xa8, 0xe7, 0x4b, 0x3a, 0x8a, 0x19, 0x19, 0x42, 0xd6, 0x74, 0x5d, 0xfc, 0x92, 0x4c, 0x9e, + 0x41, 0x65, 0x88, 0x17, 0x2c, 0x9e, 0xd0, 0xa3, 0xdd, 0x55, 0x36, 0xe6, 0x6a, 0xca, 0x20, 0xf4, + 0x0c, 0xde, 0x61, 0xdc, 0xe1, 0x53, 0x26, 0xc5, 0xcc, 0x9d, 0xbe, 0xbf, 0x3b, 0xdc, 0xe0, 0x5e, + 0x57, 0x42, 0xcd, 0x38, 0x44, 0xef, 0xc0, 0xfc, 0x2d, 0x72, 0xa0, 0x13, 0x98, 0x66, 0xa2, 0x14, + 0xe7, 0x38, 0x8c, 0x8f, 0x74, 0xe5, 0x40, 0x05, 0x78, 0x37, 0xc4, 0x13, 0x1a, 0x8a, 0xc7, 0x68, + 0x80, 0x4b, 0x5b, 0xff, 0x33, 0x05, 0xdf, 0x35, 0xac, 0xea, 0x2a, 0xa9, 0x14, 0xe1, 0x21, 0xcc, + 0x4a, 0xde, 0x76, 0x30, 0x1d, 0xbb, 0x71, 0x4a, 0xc5, 0xcc, 0x48, 0x5f, 0x5b, 0xba, 0x50, 0x03, + 0x16, 0x5d, 0xcc, 0xb8, 0xcd, 0x96, 0x14, 0xe5, 0x89, 0xba, 0x23, 0xea, 0x0d, 0x6d, 0x1f, 0x93, + 0x81, 0xcf, 0x65, 0x31, 0xc5, 0x3c, 0x11, 0xb8, 0x95, 0x12, 0x06, 0xf7, 0x0c, 0x01, 0x6a, 0x4a, + 0x0c, 0xfa, 0x06, 0x40, 0xed, 0x1f, 0x12, 0x89, 0x53, 0x4b, 0xfd, 0x0f, 0xa7, 0x56, 0xd8, 0xd1, + 0x84, 0xc3, 0x7c, 0x34, 0x84, 0x27, 0xdb, 0x1d, 0xac, 0x2d, 0x38, 0x53, 0x95, 0x7d, 0x97, 0x69, + 0xab, 0xd8, 0xda, 0x33, 0x43, 0xdf, 0x02, 0xf8, 0xc1, 0x76, 0xb5, 0x1b, 0x67, 0x61, 0x8f, 0x08, + 0xe3, 0xea, 0x81, 0xac, 0xbb, 0xe7, 0x65, 0xe8, 0x9b, 0xb5, 0xbf, 0xdc, 0xba, 0x93, 0x17, 0x84, + 0xf1, 0x0f, 0x7f, 0x02, 0x30, 0xbd, 0xdc, 0x2d, 0xf4, 0x18, 0xbe, 0x57, 0x3f, 0xef, 0x54, 0x9b, + 0x76, 0xd7, 0x3a, 0xb3, 0x5e, 0x75, 0xed, 0xee, 0x2b, 0xe3, 0x65, 0xcb, 0xb2, 0xea, 0xb5, 0xa3, + 0x44, 0xe1, 0xf0, 0x87, 0x9f, 0x8b, 0xe9, 0x6e, 0xbc, 0x49, 0xbd, 0x1b, 0xd0, 0x6a, 0xa7, 0xdd, + 0x68, 0x99, 0x2f, 0xeb, 0xb5, 0x23, 0x10, 0x41, 0xab, 0xd1, 0x3f, 0xeb, 0x2d, 0xd0, 0x46, 0xab, + 0x7d, 0xf6, 0xa2, 0xf5, 0xba, 0x5e, 0x3b, 0x4a, 0x46, 0xd0, 0x06, 0x09, 0x9c, 0x11, 0xf9, 0x1a, + 0xf7, 0x0a, 0xca, 0xf7, 0xbf, 0x68, 0x09, 0xa3, 0xf3, 0xeb, 0xa5, 0x06, 0xde, 0x5c, 0x6a, 0xe0, + 0x8f, 0x4b, 0x0d, 0xfc, 0x78, 0xa5, 0x25, 0xde, 0x5c, 0x69, 0x89, 0xdf, 0xaf, 0xb4, 0xc4, 0xeb, + 0xcf, 0xfe, 0x6d, 0xea, 0xf3, 0xad, 0x0f, 0xa2, 0xdc, 0x02, 0xf7, 0x8e, 0xfc, 0xac, 0x7d, 0xf2, + 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x55, 0x92, 0x5f, 0x36, 0x07, 0x00, 0x00, } func (m *BTCSpvProof) Marshal() (dAtA []byte, err error) { @@ -914,10 +914,10 @@ func (m *EpochData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x10 } - if len(m.Key) > 0 { - for iNdEx := len(m.Key) - 1; iNdEx >= 0; iNdEx-- { + if len(m.Keys) > 0 { + for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.Key[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Keys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1156,8 +1156,8 @@ func (m *EpochData) Size() (n int) { } var l int _ = l - if len(m.Key) > 0 { - for _, e := range m.Key { + if len(m.Keys) > 0 { + for _, e := range m.Keys { l = e.Size() n += 1 + l + sovBtccheckpoint(uint64(l)) } @@ -1906,7 +1906,7 @@ func (m *EpochData) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1933,8 +1933,8 @@ func (m *EpochData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Key = append(m.Key, &SubmissionKey{}) - if err := m.Key[len(m.Key)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Keys = append(m.Keys, &SubmissionKey{}) + if err := m.Keys[len(m.Keys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/btccheckpoint/types/btcutils.go b/x/btccheckpoint/types/btcutils.go index ce34fda7e..37670301f 100644 --- a/x/btccheckpoint/types/btcutils.go +++ b/x/btccheckpoint/types/btcutils.go @@ -8,9 +8,9 @@ import ( "github.com/babylonchain/babylon/types" "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/btcutil" ) const ( @@ -25,7 +25,7 @@ const ( maxOpReturnPkScriptSize = 83 ) -// Parsed proof represent semantically valid: +// ParsedProof represent semantically valid: // - Bitcoin Header // - Bitcoin Header hash // - Bitcoin Transaction diff --git a/x/btccheckpoint/types/btcutils_test.go b/x/btccheckpoint/types/btcutils_test.go index 1f27123ed..576929eea 100644 --- a/x/btccheckpoint/types/btcutils_test.go +++ b/x/btccheckpoint/types/btcutils_test.go @@ -11,16 +11,6 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" ) -//nolint:unused -func hashFromString(s string) *chainhash.Hash { - hash, e := chainhash.NewHashFromStr(s) - if e != nil { - panic("Invalid hex sting") - } - - return hash -} - // Sanity test checking mostly btcd code, that we can realy parse bitcoin transaction func TestBtcTransactionParsing(t *testing.T) { // Few randomly chosed btc valid btc transactions diff --git a/x/btccheckpoint/types/errors.go b/x/btccheckpoint/types/errors.go index 37318dce5..64a3dd681 100644 --- a/x/btccheckpoint/types/errors.go +++ b/x/btccheckpoint/types/errors.go @@ -1,7 +1,5 @@ package types -// DONTCOVER - import ( errorsmod "cosmossdk.io/errors" ) @@ -12,7 +10,6 @@ var ( ErrDuplicatedSubmission = errorsmod.Register(ModuleName, 1101, "Duplicated submission") ErrNoCheckpointsForPreviousEpoch = errorsmod.Register(ModuleName, 1102, "No checkpoints for previous epoch") ErrInvalidHeader = errorsmod.Register(ModuleName, 1103, "Proof headers are invalid") - ErrProvidedHeaderFromDifferentForks = errorsmod.Register(ModuleName, 1104, "Proof header from different forks") - ErrProvidedHeaderDoesNotHaveAncestor = errorsmod.Register(ModuleName, 1105, "Proof header does not have ancestor in previous epoch") - ErrEpochAlreadyFinalized = errorsmod.Register(ModuleName, 1106, "Submission denied. Epoch already finalized") + ErrProvidedHeaderDoesNotHaveAncestor = errorsmod.Register(ModuleName, 1104, "Proof header does not have ancestor in previous epoch") + ErrEpochAlreadyFinalized = errorsmod.Register(ModuleName, 1105, "Submission denied. Epoch already finalized") ) diff --git a/x/btccheckpoint/types/expected_keepers.go b/x/btccheckpoint/types/expected_keepers.go index d351b6a5b..1d9da3675 100644 --- a/x/btccheckpoint/types/expected_keepers.go +++ b/x/btccheckpoint/types/expected_keepers.go @@ -1,38 +1,23 @@ package types import ( + "context" txformat "github.com/babylonchain/babylon/btctxformatter" bbn "github.com/babylonchain/babylon/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// AccountKeeper defines the expected account keeper used for simulations (noalias) -type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI - // Methods imported from account should be defined here -} - -// BankKeeper defines the expected interface needed to retrieve account balances. -type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here -} - type BTCLightClientKeeper interface { // BlockHeight should validate if header with given hash is valid and if it is // part of known chain. In case this is true it should return this block height // in case this is false it should return error - BlockHeight(ctx sdk.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) + BlockHeight(ctx context.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) - // MainChainDepth returns the depth of the header in the main chain or -1 if it does not exist in it - // Error is returned if header is unknown to lightclient - MainChainDepth(ctx sdk.Context, headerBytes *bbn.BTCHeaderHashBytes) (int64, error) + // MainChainDepth returns the depth of the header in the main chain or error if the header does not exist + MainChainDepth(ctx context.Context, headerBytes *bbn.BTCHeaderHashBytes) (uint64, error) } type CheckpointingKeeper interface { - VerifyCheckpoint(ctx sdk.Context, checkpoint txformat.RawBtcCheckpoint) error + VerifyCheckpoint(ctx context.Context, checkpoint txformat.RawBtcCheckpoint) error // It quite mouthfull to have 4 different methods to operate on checkpoint state // but this approach decouples both modules a bit more than having some kind // of shared enum passed into the methods. Both modules are free to evolve their @@ -40,15 +25,19 @@ type CheckpointingKeeper interface { // SetCheckpointSubmitted informs checkpointing module that checkpoint was // successfully submitted on btc chain. - SetCheckpointSubmitted(ctx sdk.Context, epoch uint64) + SetCheckpointSubmitted(ctx context.Context, epoch uint64) // SetCheckpointConfirmed informs checkpointing module that checkpoint was // successfully submitted on btc chain, and it is at least K-deep on the main chain - SetCheckpointConfirmed(ctx sdk.Context, epoch uint64) + SetCheckpointConfirmed(ctx context.Context, epoch uint64) // SetCheckpointFinalized informs checkpointing module that checkpoint was // successfully submitted on btc chain, and it is at least W-deep on the main chain - SetCheckpointFinalized(ctx sdk.Context, epoch uint64) + SetCheckpointFinalized(ctx context.Context, epoch uint64) // SetCheckpointForgotten informs checkpointing module that this checkpoint lost // all submissions on btc chain - SetCheckpointForgotten(ctx sdk.Context, epoch uint64) + SetCheckpointForgotten(ctx context.Context, epoch uint64) +} + +type IncentiveKeeper interface { + RewardBTCTimestamping(ctx context.Context, epoch uint64, rewardDistInfo *RewardDistInfo) } diff --git a/x/btccheckpoint/types/genesis.go b/x/btccheckpoint/types/genesis.go index a6cdfe807..2fb67af14 100644 --- a/x/btccheckpoint/types/genesis.go +++ b/x/btccheckpoint/types/genesis.go @@ -1,8 +1,5 @@ package types -// DefaultIndex is the default capability global index -const DefaultIndex uint64 = 1 - // DefaultGenesis returns the default Capability genesis state func DefaultGenesis() *GenesisState { return &GenesisState{ diff --git a/x/btccheckpoint/types/incentive.go b/x/btccheckpoint/types/incentive.go new file mode 100644 index 000000000..d426a8d8a --- /dev/null +++ b/x/btccheckpoint/types/incentive.go @@ -0,0 +1,47 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CheckpointAddressPair is a pair of (submitter, reporter) addresses of a checkpoint +// submission +type CheckpointAddressPair struct { + Submitter sdk.AccAddress + Reporter sdk.AccAddress +} + +func NewCheckpointAddressPair(addrs *CheckpointAddresses) (*CheckpointAddressPair, error) { + var ( + submitter sdk.AccAddress + reporter sdk.AccAddress + ) + if err := submitter.Unmarshal(addrs.Submitter); err != nil { + return nil, fmt.Errorf("failed to unmarshal submitter address in bytes: %w", err) + } + if err := reporter.Unmarshal(addrs.Reporter); err != nil { + return nil, fmt.Errorf("failed to unmarshal reporter address in bytes: %w", err) + } + return &CheckpointAddressPair{ + Submitter: submitter, + Reporter: reporter, + }, nil +} + +// RewardDistInfo includes information necessary for incentive module to distribute rewards to +// a given finalised epoch +type RewardDistInfo struct { + // Best is the address pair of the best checkpoint submission + Best *CheckpointAddressPair + // Others is a list of other address pairs + Others []*CheckpointAddressPair +} + +func NewRewardDistInfo(best *CheckpointAddressPair, others ...*CheckpointAddressPair) *RewardDistInfo { + return &RewardDistInfo{ + Best: best, + Others: others, + } +} diff --git a/x/btccheckpoint/types/mock_keepers.go b/x/btccheckpoint/types/mock_keepers.go index c541246b7..3d533d24f 100644 --- a/x/btccheckpoint/types/mock_keepers.go +++ b/x/btccheckpoint/types/mock_keepers.go @@ -1,24 +1,27 @@ package types import ( + "context" "errors" txformat "github.com/babylonchain/babylon/btctxformatter" bbn "github.com/babylonchain/babylon/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) type MockBTCLightClientKeeper struct { - headers map[string]int64 + headers map[string]uint64 } type MockCheckpointingKeeper struct { returnError bool } +type MockIncentiveKeeper struct { +} + func NewMockBTCLightClientKeeper() *MockBTCLightClientKeeper { lc := MockBTCLightClientKeeper{ - headers: make(map[string]int64), + headers: make(map[string]uint64), } return &lc } @@ -30,6 +33,10 @@ func NewMockCheckpointingKeeper() *MockCheckpointingKeeper { return &mc } +func NewMockIncentiveKeeper() *MockIncentiveKeeper { + return &MockIncentiveKeeper{} +} + func (mc *MockCheckpointingKeeper) ReturnError() { mc.returnError = true } @@ -38,16 +45,20 @@ func (mc *MockCheckpointingKeeper) ReturnSuccess() { mc.returnError = false } -func (mc *MockBTCLightClientKeeper) SetDepth(header *bbn.BTCHeaderHashBytes, dd int64) { +func (mc *MockBTCLightClientKeeper) SetDepth(header *bbn.BTCHeaderHashBytes, dd uint64) { mc.headers[header.String()] = dd } -func (mb MockBTCLightClientKeeper) BlockHeight(ctx sdk.Context, header *bbn.BTCHeaderHashBytes) (uint64, error) { +func (mc *MockBTCLightClientKeeper) DeleteHeader(header *bbn.BTCHeaderHashBytes) { + delete(mc.headers, header.String()) +} + +func (mb MockBTCLightClientKeeper) BlockHeight(ctx context.Context, header *bbn.BTCHeaderHashBytes) (uint64, error) { // todo not used return uint64(10), nil } -func (ck MockBTCLightClientKeeper) MainChainDepth(ctx sdk.Context, headerBytes *bbn.BTCHeaderHashBytes) (int64, error) { +func (ck MockBTCLightClientKeeper) MainChainDepth(ctx context.Context, headerBytes *bbn.BTCHeaderHashBytes) (uint64, error) { depth, ok := ck.headers[headerBytes.String()] if ok { return depth, nil @@ -56,7 +67,7 @@ func (ck MockBTCLightClientKeeper) MainChainDepth(ctx sdk.Context, headerBytes * } } -func (ck MockCheckpointingKeeper) VerifyCheckpoint(ctx sdk.Context, checkpoint txformat.RawBtcCheckpoint) error { +func (ck MockCheckpointingKeeper) VerifyCheckpoint(ctx context.Context, checkpoint txformat.RawBtcCheckpoint) error { if ck.returnError { return errors.New("bad checkpoints") } @@ -66,20 +77,23 @@ func (ck MockCheckpointingKeeper) VerifyCheckpoint(ctx sdk.Context, checkpoint t // SetCheckpointSubmitted Informs checkpointing module that checkpoint was // successfully submitted on btc chain. -func (ck MockCheckpointingKeeper) SetCheckpointSubmitted(ctx sdk.Context, epoch uint64) { +func (ck MockCheckpointingKeeper) SetCheckpointSubmitted(ctx context.Context, epoch uint64) { } // SetCheckpointConfirmed Informs checkpointing module that checkpoint was // successfully submitted on btc chain, and it is at least K-deep on the main chain -func (ck MockCheckpointingKeeper) SetCheckpointConfirmed(ctx sdk.Context, epoch uint64) { +func (ck MockCheckpointingKeeper) SetCheckpointConfirmed(ctx context.Context, epoch uint64) { } // SetCheckpointFinalized Informs checkpointing module that checkpoint was // successfully submitted on btc chain, and it is at least W-deep on the main chain -func (ck MockCheckpointingKeeper) SetCheckpointFinalized(ctx sdk.Context, epoch uint64) { +func (ck MockCheckpointingKeeper) SetCheckpointFinalized(ctx context.Context, epoch uint64) { } // SetCheckpointForgotten Informs checkpointing module that was in submitted state // lost all its checkpoints and is checkpoint empty -func (ck MockCheckpointingKeeper) SetCheckpointForgotten(ctx sdk.Context, epoch uint64) { +func (ck MockCheckpointingKeeper) SetCheckpointForgotten(ctx context.Context, epoch uint64) { +} + +func (ik *MockIncentiveKeeper) RewardBTCTimestamping(ctx context.Context, epoch uint64, rewardDistInfo *RewardDistInfo) { } diff --git a/x/btccheckpoint/types/msgs.go b/x/btccheckpoint/types/msgs.go index 8ddca7c89..312f7cc68 100644 --- a/x/btccheckpoint/types/msgs.go +++ b/x/btccheckpoint/types/msgs.go @@ -5,8 +5,6 @@ import ( fmt "fmt" "math/big" - errorsmod "cosmossdk.io/errors" - txformat "github.com/babylonchain/babylon/btctxformatter" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -18,7 +16,7 @@ var ( _ sdk.Msg = (*MsgUpdateParams)(nil) ) -// Parse and Validate transactions which should contain OP_RETURN data. +// ParseTwoProofs Parse and Validate transactions which should contain OP_RETURN data. // OP_RETURN bytes are not validated in any way. It is up to the caller attach // semantic meaning and validity to those bytes. // Returned ParsedProofs are in same order as raw proofs @@ -109,43 +107,8 @@ func ParseSubmission( return sub, nil } -func (m *MsgInsertBTCSpvProof) ValidateBasic() error { - // m.Proofs are validated in ante-handler - _, err := sdk.AccAddressFromBech32(m.Submitter) - - if err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid submitter address: %s", err) - } - - return nil -} - -func (m *MsgInsertBTCSpvProof) GetSigners() []sdk.AccAddress { - // cosmos-sdk modules usually ignore possible error here, we panic for the sake - // of informing something terrible had happend - - submitter, err := sdk.AccAddressFromBech32(m.Submitter) - if err != nil { - // Panic, since the GetSigners method is called after ValidateBasic - // which performs the same check. - panic(err) - } - - return []sdk.AccAddress{submitter} -} - -// 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 } diff --git a/x/btccheckpoint/types/params.go b/x/btccheckpoint/types/params.go index 0a33579d6..6a72e6e32 100644 --- a/x/btccheckpoint/types/params.go +++ b/x/btccheckpoint/types/params.go @@ -2,7 +2,7 @@ package types import ( "encoding/hex" - fmt "fmt" + "fmt" txformat "github.com/babylonchain/babylon/btctxformatter" ) diff --git a/x/btccheckpoint/types/tx.pb.go b/x/btccheckpoint/types/tx.pb.go index d36a3a6ba..f02e60b7d 100644 --- a/x/btccheckpoint/types/tx.pb.go +++ b/x/btccheckpoint/types/tx.pb.go @@ -229,34 +229,35 @@ func init() { func init() { proto.RegisterFile("babylon/btccheckpoint/v1/tx.proto", fileDescriptor_69a562325f8b35c5) } var fileDescriptor_69a562325f8b35c5 = []byte{ - // 425 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0xcb, 0xaa, 0xd3, 0x40, - 0x18, 0xce, 0x9c, 0x23, 0x85, 0xce, 0x11, 0xc5, 0x50, 0x38, 0x69, 0x28, 0x31, 0x06, 0x0a, 0x55, - 0x34, 0xa1, 0x15, 0xbb, 0x10, 0x14, 0x8c, 0x2b, 0x17, 0xc5, 0x92, 0xea, 0xc6, 0x8d, 0x24, 0xe9, - 0x38, 0x09, 0x36, 0x99, 0x61, 0xfe, 0x69, 0x69, 0x71, 0xe7, 0x13, 0xb8, 0xf5, 0x2d, 0x5c, 0xf8, - 0x10, 0x5d, 0x16, 0x57, 0xae, 0x44, 0xda, 0x85, 0x5b, 0x1f, 0x41, 0x9a, 0x0b, 0xbd, 0xd8, 0x88, - 0x67, 0x37, 0xff, 0xcc, 0x77, 0x9b, 0x8f, 0x1f, 0xdf, 0x09, 0xfc, 0x60, 0x31, 0x61, 0xa9, 0x13, - 0xc8, 0x30, 0x8c, 0x48, 0xf8, 0x9e, 0xb3, 0x38, 0x95, 0xce, 0xac, 0xeb, 0xc8, 0xb9, 0xcd, 0x05, - 0x93, 0x4c, 0xd5, 0x0a, 0x88, 0x7d, 0x00, 0xb1, 0x67, 0x5d, 0xfd, 0x7e, 0x25, 0xf9, 0x10, 0x9a, - 0xe9, 0xe8, 0xcd, 0x90, 0x41, 0xc2, 0xe0, 0x6d, 0x36, 0x39, 0xf9, 0x50, 0x3c, 0x5d, 0xe6, 0x93, - 0x93, 0x00, 0xdd, 0xb2, 0x13, 0xa0, 0xc5, 0x43, 0xbb, 0xd2, 0x81, 0xfb, 0xc2, 0x4f, 0x4a, 0x7e, - 0x83, 0x32, 0xca, 0x72, 0xdd, 0xed, 0x29, 0xbf, 0xb5, 0x00, 0x37, 0x06, 0x40, 0x5f, 0xa4, 0x40, - 0x84, 0x74, 0x5f, 0x3d, 0x1f, 0xf1, 0xd9, 0x50, 0x30, 0xf6, 0x4e, 0x6d, 0xe1, 0x3a, 0x4c, 0x83, - 0x24, 0x96, 0x92, 0x08, 0x0d, 0x99, 0xa8, 0x53, 0xf7, 0x76, 0x17, 0xea, 0x13, 0x5c, 0xe3, 0x5b, - 0x18, 0x68, 0x67, 0xe6, 0x79, 0xe7, 0xa2, 0xd7, 0xb6, 0xab, 0xfe, 0x6f, 0xef, 0x89, 0x7a, 0x05, - 0xc9, 0x32, 0x70, 0xeb, 0x94, 0xa9, 0x47, 0x80, 0xb3, 0x14, 0x88, 0xf5, 0x19, 0xe1, 0x9b, 0x03, - 0xa0, 0xaf, 0xf9, 0xd8, 0x97, 0x64, 0x98, 0x7d, 0x42, 0xed, 0xe3, 0xba, 0x3f, 0x95, 0x11, 0x13, - 0xb1, 0x5c, 0xe4, 0x81, 0x5c, 0xed, 0xdb, 0xd7, 0x07, 0x8d, 0xa2, 0xa3, 0x67, 0xe3, 0xb1, 0x20, - 0x00, 0x23, 0x29, 0xe2, 0x94, 0x7a, 0x3b, 0xa8, 0xfa, 0x14, 0xd7, 0xf2, 0x1a, 0xb4, 0x33, 0x13, - 0x75, 0x2e, 0x7a, 0x66, 0x75, 0xd4, 0xdc, 0xc9, 0xbd, 0xb6, 0xfc, 0x71, 0x5b, 0xf1, 0x0a, 0xd6, - 0xe3, 0x1b, 0x1f, 0x7f, 0x7d, 0xb9, 0xb7, 0xd3, 0xb3, 0x9a, 0xf8, 0xf2, 0x28, 0x5a, 0x19, 0xbb, - 0xf7, 0x1b, 0xe1, 0xf3, 0x01, 0x50, 0xf5, 0x03, 0xbe, 0xf5, 0x77, 0xa1, 0x76, 0xb5, 0xef, 0xa9, - 0x2e, 0xf4, 0xfe, 0xd5, 0xf0, 0x65, 0x08, 0x75, 0x82, 0xaf, 0x1f, 0xf4, 0x76, 0xf7, 0x9f, 0x3a, - 0xfb, 0x50, 0xbd, 0xfb, 0xdf, 0xd0, 0xd2, 0xcd, 0x7d, 0xb9, 0x5c, 0x1b, 0x68, 0xb5, 0x36, 0xd0, - 0xcf, 0xb5, 0x81, 0x3e, 0x6d, 0x0c, 0x65, 0xb5, 0x31, 0x94, 0xef, 0x1b, 0x43, 0x79, 0xf3, 0x88, - 0xc6, 0x32, 0x9a, 0x06, 0x76, 0xc8, 0x12, 0xa7, 0x90, 0x0d, 0x23, 0x3f, 0x4e, 0xcb, 0xc1, 0x99, - 0x1f, 0xed, 0xab, 0x5c, 0x70, 0x02, 0x41, 0x2d, 0x5b, 0xcb, 0x87, 0x7f, 0x02, 0x00, 0x00, 0xff, - 0xff, 0x53, 0xca, 0xbd, 0x84, 0x74, 0x03, 0x00, 0x00, + // 439 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, 0x4f, 0x2a, 0x49, 0x4e, 0xce, 0x48, 0x4d, 0xce, 0x2e, 0xc8, 0xcf, 0xcc, + 0x2b, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, + 0x2a, 0xd1, 0x43, 0x51, 0xa2, 0x57, 0x66, 0x28, 0xa5, 0x83, 0x53, 0x33, 0xaa, 0x52, 0xb0, 0x39, + 0x52, 0x92, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xf1, 0x60, 0x9e, 0x3e, 0x84, 0x03, 0x95, 0x12, + 0x87, 0xf0, 0xf4, 0x73, 0x8b, 0xd3, 0x41, 0xba, 0x73, 0x8b, 0xd3, 0xa1, 0x12, 0xaa, 0x38, 0x6d, + 0x28, 0x48, 0x2c, 0x4a, 0xcc, 0x85, 0xe9, 0x17, 0x49, 0xcf, 0x4f, 0xcf, 0x87, 0x98, 0x0b, 0x62, + 0x41, 0x44, 0x95, 0x9a, 0x19, 0xb9, 0x44, 0x7c, 0x8b, 0xd3, 0x3d, 0xf3, 0x8a, 0x53, 0x8b, 0x4a, + 0x9c, 0x42, 0x9c, 0x83, 0x0b, 0xca, 0x02, 0x8a, 0xf2, 0xf3, 0xd3, 0x84, 0x64, 0xb8, 0x38, 0x8b, + 0x4b, 0x93, 0x72, 0x33, 0x4b, 0x4a, 0x52, 0x8b, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x10, + 0x02, 0x42, 0xb6, 0x5c, 0x6c, 0x05, 0x20, 0x65, 0xc5, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, + 0xaa, 0x7a, 0xb8, 0x02, 0x40, 0x0f, 0xc9, 0xd0, 0x20, 0xa8, 0x26, 0x2b, 0xbe, 0xa6, 0xe7, 0x1b, + 0xb4, 0x10, 0xc6, 0x29, 0xc9, 0x71, 0xc9, 0x60, 0x73, 0x44, 0x50, 0x6a, 0x71, 0x41, 0x7e, 0x5e, + 0x71, 0xaa, 0xd2, 0x4c, 0x46, 0x2e, 0x7e, 0xdf, 0xe2, 0xf4, 0xd0, 0x82, 0x94, 0xc4, 0x92, 0xd4, + 0x00, 0xb0, 0xaf, 0x84, 0xcc, 0xb8, 0x38, 0x13, 0x4b, 0x4b, 0x32, 0xf2, 0x8b, 0x32, 0x4b, 0x2a, + 0x21, 0x0e, 0x74, 0x92, 0xb8, 0xb4, 0x45, 0x57, 0x04, 0x1a, 0x68, 0x8e, 0x29, 0x29, 0x45, 0xa9, + 0xc5, 0xc5, 0xc1, 0x25, 0x45, 0x99, 0x79, 0xe9, 0x41, 0x08, 0xa5, 0x42, 0x76, 0x5c, 0x6c, 0x90, + 0x70, 0x91, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xc0, 0xed, 0x74, 0x88, 0x4d, 0x4e, 0x2c, + 0x27, 0xee, 0xc9, 0x33, 0x04, 0x41, 0x75, 0x41, 0xdd, 0x0e, 0x37, 0x4f, 0x49, 0x92, 0x4b, 0x1c, + 0xcd, 0x69, 0x30, 0x67, 0x1b, 0x7d, 0x67, 0xe4, 0x62, 0xf6, 0x2d, 0x4e, 0x17, 0xaa, 0xe6, 0x12, + 0xc4, 0x0c, 0x60, 0x3d, 0xdc, 0xf6, 0x62, 0x0b, 0x0b, 0x29, 0x33, 0xd2, 0xd4, 0xc3, 0x1c, 0x21, + 0x94, 0xc3, 0xc5, 0x83, 0x12, 0x6e, 0x9a, 0x78, 0xcd, 0x41, 0x56, 0x2a, 0x65, 0x48, 0xb4, 0x52, + 0x98, 0x6d, 0x52, 0xac, 0x0d, 0xcf, 0x37, 0x68, 0x31, 0x3a, 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, 0xf4, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x18, 0x47, 0xbf, 0x02, 0x2d, 0x1d, 0x97, + 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x93, 0xab, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x2e, 0x80, 0x63, 0x8c, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/btccheckpoint/types/types.go b/x/btccheckpoint/types/types.go index bec28894e..e825908de 100644 --- a/x/btccheckpoint/types/types.go +++ b/x/btccheckpoint/types/types.go @@ -3,6 +3,7 @@ package types import ( "encoding/hex" "fmt" + "math/big" "github.com/babylonchain/babylon/btctxformatter" "github.com/babylonchain/babylon/types" @@ -10,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Semantically valid checkpoint submission with: +// RawCheckpointSubmission Semantically valid checkpoint submission with: // - valid submitter address // - at least 2 parsed proof // Modelling proofs as separate Proof1 and Proof2, as this is more explicit than @@ -81,22 +82,22 @@ func toTransactionKey(p *ParsedProof) TransactionKey { } } -func (rsc *RawCheckpointSubmission) GetSubmissionKey() SubmissionKey { +func (s *RawCheckpointSubmission) GetSubmissionKey() SubmissionKey { var keys []*TransactionKey - k1 := toTransactionKey(&rsc.Proof1) + k1 := toTransactionKey(&s.Proof1) keys = append(keys, &k1) - k2 := toTransactionKey(&rsc.Proof2) + k2 := toTransactionKey(&s.Proof2) keys = append(keys, &k2) return SubmissionKey{ Key: keys, } } -func (rsc *RawCheckpointSubmission) GetSubmissionData(epochNum uint64, txsInfo []*TransactionInfo) SubmissionData { +func (s *RawCheckpointSubmission) GetSubmissionData(epochNum uint64, txsInfo []*TransactionInfo) SubmissionData { return SubmissionData{ VigilanteAddresses: &CheckpointAddresses{ - Reporter: rsc.Reporter.Bytes(), - Submitter: rsc.CheckpointData.SubmitterAddress, + Reporter: s.Reporter.Bytes(), + Submitter: s.CheckpointData.SubmitterAddress, }, TxsInfo: txsInfo, Epoch: epochNum, @@ -116,14 +117,14 @@ func (sk *SubmissionKey) GetKeyBlockHashes() []*types.BTCHeaderHashBytes { func NewEmptyEpochData() EpochData { return EpochData{ - Key: []*SubmissionKey{}, + Keys: []*SubmissionKey{}, Status: Submitted, } } func (s *EpochData) AppendKey(k SubmissionKey) { key := &k - s.Key = append(s.Key, key) + s.Keys = append(s.Keys, key) } // HappenedAfter returns true if `this` submission happened after `that` submission @@ -161,6 +162,37 @@ func NewTransactionInfo(txKey *TransactionKey, txBytes []byte, proof []byte) *Tr } } +func NewTransactionInfoFromSpvProof(proof *BTCSpvProof) *TransactionInfo { + return &TransactionInfo{ + Key: &TransactionKey{ + Index: proof.BtcTransactionIndex, + Hash: proof.ConfirmingBtcHeader.Hash(), + }, + Transaction: proof.BtcTransaction, + Proof: proof.MerkleNodes, + } +} + +func NewTransactionInfoFromHex(txInfoHex string) (*TransactionInfo, error) { + txInfoBytes, err := hex.DecodeString(txInfoHex) + if err != nil { + return nil, err + } + var txInfo TransactionInfo + if err := txInfo.Unmarshal(txInfoBytes); err != nil { + return nil, err + } + return &txInfo, nil +} + +func (ti *TransactionInfo) ToHexStr() (string, error) { + txInfoBytes, err := ti.Marshal() + if err != nil { + return "", err + } + return hex.EncodeToString(txInfoBytes), nil +} + func (ti *TransactionInfo) ValidateBasic() error { if ti.Key == nil { return fmt.Errorf("key in TransactionInfo is nil") @@ -174,6 +206,34 @@ func (ti *TransactionInfo) ValidateBasic() error { return nil } +// VerifyInclusion verifies the tx is included in a given BTC header that satisfies the given PoW limit +// TODO: given that TransactionInfo is now used in btcstaking module as well, +// probably we need to move it out from btccheckpoint +func (ti *TransactionInfo) VerifyInclusion(btcHeader *types.BTCHeaderBytes, powLimit *big.Int) error { + if err := ti.ValidateBasic(); err != nil { + return err + } + if !ti.Key.Hash.Eq(btcHeader.Hash()) { + return fmt.Errorf("the given btcHeader is different from that in TransactionInfo") + } + + tx, err := ParseTransaction(ti.Transaction) + if err != nil { + return err + } + + header := btcHeader.ToBlockHeader() + if err := types.ValidateBTCHeader(header, powLimit); err != nil { + return err + } + + if !verify(tx, &header.MerkleRoot, ti.Proof, ti.Key.Index) { + return fmt.Errorf("header failed validation due to failed proof") + } + + return nil +} + func NewSpvProofFromHexBytes(c codec.Codec, proof string) (*BTCSpvProof, error) { bytes, err := hex.DecodeString(proof) diff --git a/x/btclightclient/README.md b/x/btclightclient/README.md new file mode 100644 index 000000000..65609d6f1 --- /dev/null +++ b/x/btclightclient/README.md @@ -0,0 +1,259 @@ +# BTC light client + +The BTC light client module is essentially a BTC light client that maintains +the canonical header chain of Bitcoin. + +Babylon chain needs to know about different events happening on Bitcoin chain. +To make it possible in a secure way, Babylon needs to know the current +state of the Bitcoin chain i.e., what is the canonical chain of the Bitcoin network. + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Concepts](#concepts) + - [Problem statement](#problem-statement) + - [Babylon's BTC light client design](#babylons-btc-light-client-design) +- [States](#states) + - [Parameters](#parameters) + - [Headers storage](#headers-storage) + - [HashToHeight storage](#hashtoheight-storage) +- [Messages](#messages) + - [MsgInsertHeaders](#msginsertheaders) + - [MsgUpdateParams](#msgupdateparams) +- [Hooks](#hooks) + - [Hooks exposed by BTC light client](#hooks-exposed-by-btc-light-client) +- [Events](#events) + +## Concepts + +### Problem statement + +The Babylon chain needs to learn and validate a number of events that had +happened on the Bitcoin chain. Those events are: + +1. `New checkpoint event` - Bitcoin Timestamping protocol requires checkpoints +on Bitcoin to be reported back to Babylon. To do it securely, each checkpoint +must be reported back along with the inclusion proof of transactions +which carry this checkpoint. +2. `New BTC delegation event` - Bitcoin Staking protocol requires staking +transactions to be deep enough in the Bitcoin chain. Thus, the staking +transactions also must be accompanied by the inclusion proof. + +To properly validate those inclusion proofs, the Babylon chain needs to know the +current state of BTC chain i.e., what is current canonical chain recognized by BTC. + +### Babylon's BTC light client design + +Babylon maintains a BTC light client so that it can verify the inclusion +of various Bitcoin transactions. + +In a high-level overview, the Babylon BTC light client starts from some base BTC +header existing on the BTC network and allows extending this header by applying the +same rules as a normal BTC node. + +The base BTC header must: +- Be deep enough in the BTC chain so that it will never be reverted by the BTC network. +- Be at a height of BTC difficulty adjustment [boundary](https://en.bitcoin.it/wiki/Difficulty#How_often_does_the_network_difficulty_change.3F). +This is required, to properly validate all future difficulty adjustments. + +The base BTC header is defined in the [genesis](../../proto/babylon/btclightclient/v1/genesis.proto) module. + +The Babylon BTC light client module stores only BTC headers from the canonical +chain, and does not store the headers on the forks. +The BTC canonical chain can only be extended by processing +valid [MsgInsertHeaders](#msginsertheaders) messages. + +If a better fork is encountered: +1. The current chain is rolled back to the parent of the received fork. +2. The chain is extend with new headers from the fork. + +A better fork is defined as the fork with higher total difficulty, summing the +difficulties for each block in the fork. + +## States + +The BTC light client module maintains the following KV stores. + +### Parameters + +The [parameter storage](./keeper/params.go) maintains the BTC light client module's +parameters. The BTC light client module's parameters are represented as a `Params` +[object](../../proto/babylon/btclightclient/v1/params.proto) defined as follows: + +```protobuf +// Params defines the parameters for the module. +message Params { + option (gogoproto.equal) = true; + + // List of addresses which are allowed to insert headers to btc light client + // if the list is empty, any address can insert headers + repeated string insert_headers_allow_list = 1; +} +``` + +In a nutshell, `insert_headers_allow_list` makes it possible to set up +restrictions about who is able to update the BTC light client module state. +If `insert_headers_allow_list` is not empty, only addresses in the list can send +`MsgInsertHeaders` messages. + +### Headers storage + +The [Headers storage](./keeper/state.go) maintains all headers on the canonical +chain of Bitcoin. +The key is the header height in the BTC chain, and the value is a `BTCHeaderInfo` +[object](../../proto/babylon/btclightclient/v1/btclightclient.proto) +which contains the BTC header along with some metadata. + +```protobuf +// BTCHeaderInfo is a structure that contains all relevant information about a +// BTC header +// - Full header bytes +// - Header hash for easy retrieval +// - Height of the header in the BTC chain +// - Total work spent on the header. This is the sum of the work corresponding +// to the header Bits field and the total work of the header. +message BTCHeaderInfo { + bytes header = 1 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/types.BTCHeaderBytes" ]; + bytes hash = 2 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" ]; + uint64 height = 3; + bytes work = 4 + [ (gogoproto.customtype) = "cosmossdk.io/math.Uint" ]; +} +``` + +### HashToHeight storage + +The [HashToHeight storage](./keeper/state.go) maintains an index in which the key is +the BTC header hash, and the value is the BTC header's height. + +This index enables efficient lookup of BTC headers by their hash. This is useful +in many situations, notably when receiving a potential chain extension which +does not point to the current BTC chain tip. + +## Messages + +### MsgInsertHeaders + +`MsgInsertHeaders` is the main message processed by the BTC light client module. +Its purpose is to update the state of the BTC chain as viewed by Babylon chain. + +The handler of this message is defined +at [x/btclightclient/keeper/msg_server.go](./keeper/msg_server.go). + +The message contains a list of BTC headers encoded in Bitcoin format. + +```proto +message MsgInsertHeaders { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + repeated bytes headers = 2 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/types.BTCHeaderBytes" ]; +} +``` + +Upon receiving a `MsgInsertHeaders` message, a Babylon node applies the following +verification rules. This is a subset of the BTC +[protocol](https://en.bitcoin.it/wiki/Protocol_rules#.22block.22_messages) rules: + +- The `headers` list must not be empty. +- The headers in the list must be connected by parent-child relationships i.e. +the header at position `i + 1`, must have its `PrevBlock` field set to header's `i` hash. +- The first header of the list must point to a header already maintained by the BTC +light client module. +- Each header must be correctly encoded. +- Each header in the list must have valid proof of work and difficulty. +- Each header in the list must have a `Timestamp` that is greater than the median +of last 11 ancestors. +- If the first header of the list does not point to the current tip of the +chain maintained by the BTC light client, it means that the message contains a fork. For +the fork to be valid, the forked chain must be better than the current chain maintained by +the BTC light client. The fork is better when its total work is greater than the work +of current the [chain](https://en.bitcoin.it/wiki/Protocol_rules#Blocks). + +All those rules are the same rules which are applied by BTC nodes when receiving +headers from the BTC network. + +Processing of the message is atomic, so, if just one header in the list is +invalid, the state of the BTC light client module won't be updated. + +In case of receiving a valid chain extension, the chain maintained by +the BTC light client module will be extended, and the received headers will be saved +in the storage. + +In case of receiving a valid and better fork, i.e., the first header of the `headers` list +does not point to the current BTC chain tip, and the fork's total work is larger than +the current BTC chain's total work, the chain maintained by the BTC light client will +be rolled back to the header that is the fork's header, and it will then be +extended with the headers received in `headers`. + +### MsgUpdateParams + +The `MsgUpdateParams` message is used for updating the module parameters for the +BTC light client module. It can only be executed via a governance proposal. + +```protobuf +// MsgUpdateParams defines a message for updating btc light client 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 btc light client parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} +``` + +## Hooks + +The BTC light client module exposes a set of hooks to inform other modules +about the updates to the maintained Bitcoin light client: + +### Hooks exposed by BTC light client + +```go +type BTCLightClientHooks interface { + AfterBTCRollBack(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled back + AfterBTCRollForward(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled forward + AfterBTCHeaderInserted(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after a header is inserted +} + +``` + +## Events + +The BTC light client module exposes a set of events about the updates to the +maintained Bitcoin best chain: + +```protobuf + +// The header included in the event is the block in the history +// of the current mainchain to which we are rolling back to. +// In other words, there is one rollback event emitted per re-org, to the +// greatest common ancestor of the old and the new fork. +message EventBTCRollBack { BTCHeaderInfo header = 1; } + +// EventBTCRollForward is emitted on Msg/InsertHeader +// The header included in the event is the one the main chain is extended with. +// In the event of a reorg, each block on the new fork that comes after +// the greatest common ancestor will have a corresponding roll forward event. +message EventBTCRollForward { BTCHeaderInfo header = 1; } + +// EventBTCHeaderInserted is emitted on Msg/InsertHeader +// The header included in the event is the one that was added to the +// on chain BTC storage. +message EventBTCHeaderInserted { BTCHeaderInfo header = 1; } + +``` + diff --git a/x/btclightclient/client/cli/query.go b/x/btclightclient/client/cli/query.go index 98360a8d0..17faccb2b 100644 --- a/x/btclightclient/client/cli/query.go +++ b/x/btclightclient/client/cli/query.go @@ -11,7 +11,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string) *cobra.Command { +func GetQueryCmd(_ string) *cobra.Command { // Group btclightclient queries under a subcommand cmd := &cobra.Command{ Use: types.ModuleName, @@ -26,6 +26,7 @@ func GetQueryCmd(queryRoute string) *cobra.Command { cmd.AddCommand(CmdMainChain()) cmd.AddCommand(CmdTip()) cmd.AddCommand(CmdBaseHeader()) + cmd.AddCommand(CmdHeaderDepth()) return cmd } @@ -56,6 +57,7 @@ func CmdHashes() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "hashes") return cmd } @@ -114,6 +116,7 @@ func CmdMainChain() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "main-chain") return cmd } @@ -167,3 +170,31 @@ func CmdBaseHeader() *cobra.Command { return cmd } + +func CmdHeaderDepth() *cobra.Command { + cmd := &cobra.Command{ + Use: "header-depth [hex-hash]", + Short: "check main chain depth of the header with the given hash", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + depthRequest, err := types.NewQueryHeaderDepthRequest(args[0]) + if err != nil { + return err + } + res, err := queryClient.HeaderDepth(context.Background(), depthRequest) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/btclightclient/client/cli/tx.go b/x/btclightclient/client/cli/tx.go index 043ef24ba..bcf27f541 100644 --- a/x/btclightclient/client/cli/tx.go +++ b/x/btclightclient/client/cli/tx.go @@ -27,8 +27,8 @@ func GetTxCmd() *cobra.Command { func CmdTxInsertHeader() *cobra.Command { cmd := &cobra.Command{ - Use: "insert-header [header-bytes]", - Short: "submit BTC header bytes", + Use: "insert-headers [headers-bytes]", + Short: "submit BTC headers bytes", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -36,7 +36,7 @@ func CmdTxInsertHeader() *cobra.Command { return err } - msg, err := types.NewMsgInsertHeader(clientCtx.GetFromAddress(), args[0]) + msg, err := types.NewMsgInsertHeaders(clientCtx.GetFromAddress(), args[0]) if err != nil { return err } diff --git a/x/btclightclient/genesis.go b/x/btclightclient/genesis.go index 3230a5052..d19d9dfee 100644 --- a/x/btclightclient/genesis.go +++ b/x/btclightclient/genesis.go @@ -1,25 +1,35 @@ package btclightclient import ( + "context" + "github.com/babylonchain/babylon/x/btclightclient/keeper" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + if err := genState.Validate(); err != nil { + panic(err) + } + k.SetBaseBTCHeader(ctx, genState.BaseBtcHeader) + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(err) + } } // ExportGenesis returns the capability module's exported genesis. -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() baseBTCHeader := k.GetBaseBTCHeader(ctx) if baseBTCHeader == nil { panic("A base BTC Header has not been set") } + genesis.BaseBtcHeader = *baseBTCHeader + genesis.Params = k.GetParams(ctx) return genesis } diff --git a/x/btclightclient/genesis_test.go b/x/btclightclient/genesis_test.go index 89e2b8b99..3f9ca2a2f 100644 --- a/x/btclightclient/genesis_test.go +++ b/x/btclightclient/genesis_test.go @@ -3,8 +3,6 @@ package btclightclient_test import ( "testing" - bbn "github.com/babylonchain/babylon/types" - keepertest "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/testutil/nullify" "github.com/babylonchain/babylon/x/btclightclient" @@ -13,14 +11,9 @@ import ( ) func TestGenesis(t *testing.T) { - headerBytes := bbn.GetBaseBTCHeaderBytes() - headerHeight := bbn.GetBaseBTCHeaderHeight() - headerHash := headerBytes.Hash() - headerWork := types.CalcWork(&headerBytes) - baseHeaderInfo := types.NewBTCHeaderInfo(&headerBytes, headerHash, headerHeight, &headerWork) - + baseHeaderInfo := types.SimnetGenesisBlock() genesisState := types.GenesisState{ - BaseBtcHeader: *baseHeaderInfo, + BaseBtcHeader: baseHeaderInfo, } k, ctx := keepertest.BTCLightClientKeeper(t) diff --git a/x/btclightclient/keeper/base_btc_header.go b/x/btclightclient/keeper/base_btc_header.go index 3e13947e6..6c860a512 100644 --- a/x/btclightclient/keeper/base_btc_header.go +++ b/x/btclightclient/keeper/base_btc_header.go @@ -1,20 +1,19 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) GetBaseBTCHeader(ctx sdk.Context) *types.BTCHeaderInfo { - return k.headersState(ctx).GetBaseBTCHeader() +func (k Keeper) GetBaseBTCHeader(ctx context.Context) *types.BTCHeaderInfo { + return k.headersState(ctx).BaseHeader() } -// SetBaseBTCHeader checks whether a base BTC header exists and -// if not inserts it into storage -func (k Keeper) SetBaseBTCHeader(ctx sdk.Context, baseBTCHeader types.BTCHeaderInfo) { - existingHeader := k.headersState(ctx).GetBaseBTCHeader() +// SetBaseBTCHeader checks whether a base BTC header exist, if not inserts it into storage +func (k Keeper) SetBaseBTCHeader(ctx context.Context, baseBTCHeader types.BTCHeaderInfo) { + existingHeader := k.headersState(ctx).BaseHeader() if existingHeader != nil { panic("A base BTC Header has already been set") } - k.headersState(ctx).CreateHeader(&baseBTCHeader) + k.headersState(ctx).insertHeader(&baseBTCHeader) } diff --git a/x/btclightclient/keeper/export_headers_state_test.go b/x/btclightclient/keeper/export_headers_state_test.go index c82276f8c..af489048f 100644 --- a/x/btclightclient/keeper/export_headers_state_test.go +++ b/x/btclightclient/keeper/export_headers_state_test.go @@ -1,7 +1,9 @@ package keeper -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "context" +) -func (k *Keeper) HeadersState(ctx sdk.Context) headersState { +func (k *Keeper) HeadersState(ctx context.Context) headersState { return k.headersState(ctx) } diff --git a/x/btclightclient/keeper/grpc_query.go b/x/btclightclient/keeper/grpc_query.go index edd0d1369..c3230435e 100644 --- a/x/btclightclient/keeper/grpc_query.go +++ b/x/btclightclient/keeper/grpc_query.go @@ -13,6 +13,15 @@ import ( var _ types.QueryServer = Keeper{} +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) Hashes(ctx context.Context, req *types.QueryHashesRequest) (*types.QueryHashesResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") @@ -99,12 +108,11 @@ func (k Keeper) MainChain(ctx context.Context, req *types.QueryMainChainRequest) var nextKey []byte if req.Pagination.Reverse { var start, end uint64 - baseHeader := k.headersState(sdkCtx).GetBaseBTCHeader() + baseHeader := k.headersState(sdkCtx).BaseHeader() // The base header is located at the end of the mainchain // which requires starting at the end - mainchain := k.headersState(sdkCtx).GetMainChain() - // Reverse the mainchain -- we want to retrieve results starting from the base header - bbn.Reverse(mainchain) + mainchain := k.GetMainChainFrom(sdkCtx, 0) + if keyHeader == nil { keyHeader = baseHeader start = 0 @@ -138,7 +146,7 @@ func (k Keeper) MainChain(ctx context.Context, req *types.QueryMainChainRequest) // -1 because the depth denotes how many headers have been built on top of it depth := startHeaderDepth + req.Pagination.Limit - 1 // Retrieve the mainchain up to the depth - mainchain := k.headersState(sdkCtx).GetMainChainUpTo(depth) + mainchain := k.GetMainChainUpTo(sdkCtx, depth) // Check whether the key provided is part of the mainchain if uint64(len(mainchain)) <= startHeaderDepth || !mainchain[startHeaderDepth].Eq(keyHeader) { return nil, status.Error(codes.InvalidArgument, "header specified by key is not a part of the mainchain") @@ -175,7 +183,30 @@ func (k Keeper) BaseHeader(ctx context.Context, req *types.QueryBaseHeaderReques sdkCtx := sdk.UnwrapSDKContext(ctx) - baseHeader := k.headersState(sdkCtx).GetBaseBTCHeader() + baseHeader := k.headersState(sdkCtx).BaseHeader() return &types.QueryBaseHeaderResponse{Header: baseHeader}, nil } + +func (k Keeper) HeaderDepth(ctx context.Context, req *types.QueryHeaderDepthRequest) (*types.QueryHeaderDepthResponse, error) { + + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + haderHash, err := bbn.NewBTCHeaderHashBytesFromHex(req.Hash) + + if err != nil { + return nil, status.Error(codes.InvalidArgument, "provided hash is not a valid hex string") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + depth, err := k.MainChainDepth(sdkCtx, &haderHash) + + if err != nil { + return nil, err + } + + return &types.QueryHeaderDepthResponse{Depth: uint64(depth)}, nil +} diff --git a/x/btclightclient/keeper/grpc_query_test.go b/x/btclightclient/keeper/grpc_query_test.go index 98f02cdaa..c5ea9e5f4 100644 --- a/x/btclightclient/keeper/grpc_query_test.go +++ b/x/btclightclient/keeper/grpc_query_test.go @@ -10,7 +10,6 @@ import ( testkeeper "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) func FuzzHashesQuery(f *testing.F) { @@ -26,8 +25,7 @@ func FuzzHashesQuery(f *testing.F) { 5. If the pagination key is not a valid hash, an error is returned. Data Generation: - - Generate a random tree of headers and insert their hashes - into the hashToHeight storage. + - Generate a random chain of headers and insert into storage. - Generate a random `limit` to the query as an integer between 1 and the total number of hashes. Do checks 2-4 by initially querying without a key and then querying @@ -37,10 +35,9 @@ func FuzzHashesQuery(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - sdkCtx := sdk.WrapSDKContext(ctx) // Test nil request - resp, err := blcKeeper.Hashes(sdkCtx, nil) + resp, err := blcKeeper.Hashes(ctx, nil) if resp != nil { t.Errorf("Nil input led to a non-nil response") } @@ -54,7 +51,7 @@ func FuzzHashesQuery(f *testing.F) { key := datagen.GenRandomByteArray(r, bzSz) pagination := constructRequestWithKey(r, key) hashesRequest := types.NewQueryHashesRequest(pagination) - resp, err = blcKeeper.Hashes(sdkCtx, hashesRequest) + resp, err = blcKeeper.Hashes(ctx, hashesRequest) if resp != nil { t.Errorf("Invalid key led to a non-nil response") } @@ -62,13 +59,23 @@ func FuzzHashesQuery(f *testing.F) { t.Errorf("Invalid key led to a nil error") } - // Generate a random tree of headers - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) + baseHeader, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+100, + ) + // Get the headers map - headersMap := tree.GetHeadersMap() - // Generate a random limit - treeSize := uint64(tree.Size()) - limit := uint64(r.Int63n(int64(tree.Size())) + 1) + headersMap := chain.GetHeadersMap() + headersMap[baseHeader.Hash.String()] = baseHeader.Header.ToBlockHeader() + + // + 1 is necessary to account for the base header + totalChainLength := chain.ChainLength() + 1 + chainSize := uint64(totalChainLength) + limit := uint64(r.Int63n(int64(totalChainLength)) + 1) // Generate a page request with a limit and a nil key pagination = constructRequestWithLimit(r, limit) // Generate the initial query @@ -77,8 +84,8 @@ func FuzzHashesQuery(f *testing.F) { // Will be used later to evaluate whether all the hashes were returned hashesFound := make(map[string]bool, 0) - for headersRetrieved := uint64(0); headersRetrieved < treeSize; headersRetrieved += limit { - resp, err = blcKeeper.Hashes(sdkCtx, hashesRequest) + for headersRetrieved := uint64(0); headersRetrieved < chainSize; headersRetrieved += limit { + resp, err = blcKeeper.Hashes(ctx, hashesRequest) if err != nil { t.Errorf("Valid request led to an error %s", err) } @@ -86,16 +93,16 @@ func FuzzHashesQuery(f *testing.F) { t.Fatalf("Valid request led to a nil response") } // If we are on the last page the elements retrieved should be equal to the remaining ones - if headersRetrieved+limit >= treeSize && uint64(len(resp.Hashes)) != treeSize-headersRetrieved { - t.Fatalf("On the last page expected %d elements but got %d", treeSize-headersRetrieved, len(resp.Hashes)) + if headersRetrieved+limit >= chainSize && uint64(len(resp.Hashes)) != chainSize-headersRetrieved { + t.Fatalf("On the last page expected %d elements but got %d", chainSize-headersRetrieved, len(resp.Hashes)) } // Otherwise, the elements retrieved should be equal to the limit - if headersRetrieved+limit < treeSize && uint64(len(resp.Hashes)) != limit { + if headersRetrieved+limit < chainSize && uint64(len(resp.Hashes)) != limit { t.Fatalf("On an intermediate page expected %d elements but got %d", limit, len(resp.Hashes)) } for _, hash := range resp.Hashes { - // Check if the hash was generated by the tree + // Check if the hash was generated by the chain if _, ok := headersMap[hash.String()]; !ok { t.Fatalf("Hashes returned a hash that was not created") } @@ -120,17 +127,16 @@ func FuzzContainsQuery(f *testing.F) { 2. The query returns true or false depending on whether the hash exists. Data generation: - - Generate a random tree of headers and insert into storage. + - Generate a random chain of headers and insert into storage. - Generate a random header but do not insert it into storage. */ datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - sdkCtx := sdk.WrapSDKContext(ctx) // Test nil input - resp, err := blcKeeper.Contains(sdkCtx, nil) + resp, err := blcKeeper.Contains(ctx, nil) if resp != nil { t.Fatalf("Nil input led to a non-nil response") } @@ -138,12 +144,19 @@ func FuzzContainsQuery(f *testing.F) { t.Errorf("Nil input led to a nil error") } - // Generate a random tree of headers and insert it into storage - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) + // Generate a random chain of headers and insert it into storage + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+100, + ) // Test with a non-existent header query, _ := types.NewQueryContainsRequest(datagen.GenRandomBTCHeaderInfo(r).Hash.MarshalHex()) - resp, err = blcKeeper.Contains(sdkCtx, query) + resp, err = blcKeeper.Contains(ctx, query) if err != nil { t.Errorf("Valid input let to an error: %s", err) } @@ -155,8 +168,8 @@ func FuzzContainsQuery(f *testing.F) { } // Test with an existing header - query, _ = types.NewQueryContainsRequest(tree.RandomNode(r).Hash.MarshalHex()) - resp, err = blcKeeper.Contains(sdkCtx, query) + query, _ = types.NewQueryContainsRequest(chain.GetRandomHeaderInfo(r).Hash.MarshalHex()) + resp, err = blcKeeper.Contains(ctx, query) if err != nil { t.Errorf("Valid input let to an error: %s", err) } @@ -182,17 +195,15 @@ func FuzzMainChainQuery(f *testing.F) { 7. End of pagination: the last elements are returned properly and the next_key is set to nil. Data Generation: - - Generate a random tree of headers with different PoW and insert them into the headers storage. - - Calculate the main chain using the `HeadersState().MainChain()` function (here we only test the query) + - Generate a random chain of headers with different PoW and insert them into the headers storage. */ datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - sdkCtx := sdk.WrapSDKContext(ctx) // Test nil input - resp, err := blcKeeper.MainChain(sdkCtx, nil) + resp, err := blcKeeper.MainChain(ctx, nil) if resp != nil { t.Errorf("Nil input led to a non-nil response") } @@ -206,7 +217,7 @@ func FuzzMainChainQuery(f *testing.F) { key := datagen.GenRandomByteArray(r, bzSz) pagination := constructRequestWithKey(r, key) mainchainRequest := types.NewQueryMainChainRequest(pagination) - resp, err = blcKeeper.MainChain(sdkCtx, mainchainRequest) + resp, err = blcKeeper.MainChain(ctx, mainchainRequest) if resp != nil { t.Errorf("Invalid key led to a non-nil response") } @@ -214,13 +225,20 @@ func FuzzMainChainQuery(f *testing.F) { t.Errorf("Invalid key led to a nil error") } - // Generate a random tree of headers and insert it into storage - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) + // Generate a random chain of headers and insert it into storage + base, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+100, + ) // Check whether the key being set to an element that does not exist leads to an error pagination = constructRequestWithKey(r, datagen.GenRandomBTCHeaderInfo(r).Hash.MustMarshal()) mainchainRequest = types.NewQueryMainChainRequest(pagination) - resp, err = blcKeeper.MainChain(sdkCtx, mainchainRequest) + resp, err = blcKeeper.MainChain(ctx, mainchainRequest) if resp != nil { t.Errorf("Key corresponding to header that does not exist led to a non-nil response") } @@ -228,27 +246,17 @@ func FuzzMainChainQuery(f *testing.F) { t.Errorf("Key corresponding to a header that does not exist led to a nil error") } - // Get the mainchain - mainchain := tree.GetMainChain() + mainchain := make([]*types.BTCHeaderInfo, 0) - // Check whether the key being set to a non-mainchain element leads to an error - // Select a random header - header := tree.RandomNode(r) - // Get the tip - tip := tree.GetTip() - // if the header is not on the mainchain, we can test our assumption - // if it is, randomness will ensure that it does on another test case - if !tree.IsOnNodeChain(tip, header) { - pagination = constructRequestWithKeyAndLimit(r, header.Hash.MustMarshal(), uint64(len(mainchain))) - mainchainRequest = types.NewQueryMainChainRequest(pagination) - resp, err = blcKeeper.MainChain(sdkCtx, mainchainRequest) - if resp != nil { - t.Errorf("Key corresponding to header that is not on the mainchain led to a non-nil response") - } - if err == nil { - t.Errorf("Key corresponding to a header that is not on the mainchain led to a nil error") - } - } + mainchain = append(mainchain, base) + mainchain = append(mainchain, chain.GetChainInfo()...) + // we need to reverse the mainchain because the query returns the mainchain from highest to lowest header + bbn.Reverse(mainchain) + // // Check whether the key being set to a non-mainchain element leads to an error + // // Select a random header + // header := chain.GetRandomHeaderInfo(r) + // // Get the tip + // tip := chah.GetTip() // Index into the current element of mainchain that we are iterating mcIdx := 0 @@ -267,7 +275,7 @@ func FuzzMainChainQuery(f *testing.F) { // Generate the initial query mainchainRequest = types.NewQueryMainChainRequest(pagination) for headersRetrieved := uint64(0); headersRetrieved < mcSize; headersRetrieved += limit { - resp, err = blcKeeper.MainChain(sdkCtx, mainchainRequest) + resp, err = blcKeeper.MainChain(ctx, mainchainRequest) if err != nil { t.Errorf("Valid request led to an error %s", err) } @@ -314,16 +322,15 @@ func FuzzTipQuery(f *testing.F) { 2. The query returns the tip BTC header Data generation: - - Generate a random tree of headers and insert into storage + - Generate a random chain of headers and insert into storage */ datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - sdkCtx := sdk.WrapSDKContext(ctx) // Test nil input - resp, err := blcKeeper.Tip(sdkCtx, nil) + resp, err := blcKeeper.Tip(ctx, nil) if resp != nil { t.Errorf("Nil input led to a non-nil response") } @@ -331,17 +338,25 @@ func FuzzTipQuery(f *testing.F) { t.Errorf("Nil input led to a nil error") } - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) + // Generate a random chain of headers and insert it into storage + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+100, + ) - resp, err = blcKeeper.Tip(sdkCtx, types.NewQueryTipRequest()) + resp, err = blcKeeper.Tip(ctx, types.NewQueryTipRequest()) if err != nil { t.Errorf("valid input led to an error: %s", err) } if resp == nil { t.Fatalf("Valid input led to nil response") } - if !resp.Header.Eq(tree.GetTip()) { - t.Errorf("Invalid header returned. Expected %s, got %s", tree.GetTip().Hash, resp.Header.Hash) + if !resp.Header.Eq(chain.GetTipInfo()) { + t.Errorf("Invalid header returned. Expected %s, got %s", chain.GetTipInfo().Hash, resp.Header.Hash) } }) } @@ -353,16 +368,15 @@ func FuzzBaseHeaderQuery(f *testing.F) { 2. The query returns the base BTC header Data generation: - - Generate a random tree of headers and insert into storage. + - Generate a random chain of headers and insert into storage. */ datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - sdkCtx := sdk.WrapSDKContext(ctx) // Test nil input - resp, err := blcKeeper.BaseHeader(sdkCtx, nil) + resp, err := blcKeeper.BaseHeader(ctx, nil) if resp != nil { t.Errorf("Nil input led to a non-nil response") } @@ -370,17 +384,25 @@ func FuzzBaseHeaderQuery(f *testing.F) { t.Errorf("Nil input led to a nil error") } - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) + // Generate a random chain of headers and insert it into storage + base, _ := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+100, + ) - resp, err = blcKeeper.BaseHeader(sdkCtx, types.NewQueryBaseHeaderRequest()) + resp, err = blcKeeper.BaseHeader(ctx, types.NewQueryBaseHeaderRequest()) if err != nil { t.Errorf("valid input led to an error: %s", err) } if resp == nil { t.Fatalf("Valid input led to nil response") } - if !resp.Header.Eq(tree.GetRoot()) { - t.Errorf("Invalid header returned. Expected %s, got %s", tree.GetRoot().Hash, resp.Header.Hash) + if !resp.Header.Eq(base) { + t.Errorf("Invalid header returned. Expected %s, got %s", base.Hash, resp.Header.Hash) } }) } diff --git a/x/btclightclient/keeper/hooks.go b/x/btclightclient/keeper/hooks.go index cc8820791..17ff31d24 100644 --- a/x/btclightclient/keeper/hooks.go +++ b/x/btclightclient/keeper/hooks.go @@ -1,29 +1,29 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // Implements BTCLightClientHooks interface var _ types.BTCLightClientHooks = Keeper{} // AfterBTCHeaderInserted - call hook if registered -func (k Keeper) AfterBTCHeaderInserted(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) AfterBTCHeaderInserted(ctx context.Context, headerInfo *types.BTCHeaderInfo) { if k.hooks != nil { k.hooks.AfterBTCHeaderInserted(ctx, headerInfo) } } // AfterBTCRollBack - call hook if registered -func (k Keeper) AfterBTCRollBack(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) AfterBTCRollBack(ctx context.Context, headerInfo *types.BTCHeaderInfo) { if k.hooks != nil { k.hooks.AfterBTCRollBack(ctx, headerInfo) } } // AfterBTCRollForward - call hook if registered -func (k Keeper) AfterBTCRollForward(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) AfterBTCRollForward(ctx context.Context, headerInfo *types.BTCHeaderInfo) { if k.hooks != nil { k.hooks.AfterBTCRollForward(ctx, headerInfo) } diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 8231ba260..78e370c6c 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -1,11 +1,15 @@ package keeper import ( + "context" "fmt" + corestoretypes "cosmossdk.io/core/store" + + "cosmossdk.io/log" bbn "github.com/babylonchain/babylon/types" - "github.com/cometbft/cometbft/libs/log" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" "github.com/babylonchain/babylon/x/btclightclient/types" "github.com/cosmos/cosmos-sdk/codec" @@ -14,27 +18,32 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - hooks types.BTCLightClientHooks - btcConfig bbn.BtcConfig + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService + hooks types.BTCLightClientHooks + btcConfig bbn.BtcConfig + bl *types.BtcLightClient + authority string } ) +var _ types.BtcChainReadStore = (*headersState)(nil) + func NewKeeper( cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, btcConfig bbn.BtcConfig, + authority string, ) Keeper { + bl := types.NewBtcLightClientFromParams(btcConfig.NetParams()) return Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - hooks: nil, - btcConfig: btcConfig, + cdc: cdc, + storeService: storeService, + hooks: nil, + btcConfig: btcConfig, + bl: bl, + authority: authority, } } @@ -52,199 +61,96 @@ func (k *Keeper) SetHooks(bh types.BTCLightClientHooks) *Keeper { return k } -// InsertHeader inserts a btcd header into the header state -func (k Keeper) InsertHeader(ctx sdk.Context, header *bbn.BTCHeaderBytes) error { - if header == nil { - return types.ErrEmptyMessage - } - headerHash := header.Hash() - parentHash := header.ParentHash() +func (k Keeper) insertHeaders( + ctx context.Context, + headers []*wire.BlockHeader, +) error { - // Check whether the header already exists, if yes reject - headerExists := k.headersState(ctx).HeaderExists(headerHash) - if headerExists { - return types.ErrDuplicateHeader - } + headerState := k.headersState(ctx) - // Check whether the parent exists, if not reject - parentExists := k.headersState(ctx).HeaderExists(parentHash) - if !parentExists { - return types.ErrHeaderParentDoesNotExist - } + result, err := k.bl.InsertHeaders( + headerState, + headers, + ) - // Retrieve the height of the parent to calculate the current height - parentHeight, err := k.headersState(ctx).GetHeaderHeight(parentHash) if err != nil { - // Height should always exist if the previous checks have passed - panic("Height for parent is not maintained") + return err } - // Retrieve the work of the parent to calculate the cumulative work - parentWork, err := k.headersState(ctx).GetHeaderWork(parentHash) - if err != nil { - // Work should always exist if the previous checks have passed - panic("Work for parent is not maintained") + // if we have rollback, first delete all headers up to the rollback point + if result.RollbackInfo != nil { + // roll back to the height + headerState.rollBackHeadersUpTo(result.RollbackInfo.HeaderToRollbackTo.Height) + // trigger rollback event + k.triggerRollBack(ctx, result.RollbackInfo.HeaderToRollbackTo) } - // Calculate the cumulative work - headerWork := types.CalcWork(header) - cumulativeWork := types.CumulativeWork(headerWork, *parentWork) - - // Construct the BTCHeaderInfo object - headerInfo := types.NewBTCHeaderInfo(header, headerHash, parentHeight+1, &cumulativeWork) - - // Retrieve the previous tip for future usage - previousTip := k.headersState(ctx).GetTip() - - // Create the header - k.headersState(ctx).CreateHeader(headerInfo) - - // Get the new tip - currentTip := k.headersState(ctx).GetTip() + for _, header := range result.HeadersToInsert { + h := header + headerState.insertHeader(h) + k.triggerHeaderInserted(ctx, h) + k.triggerRollForward(ctx, h) + } + return nil +} - // Variable maintaining the headers that have been added to the main chain - var addedToMainChain []*types.BTCHeaderInfo +func (k Keeper) InsertHeaders(ctx context.Context, headers []bbn.BTCHeaderBytes) error { + if len(headers) == 0 { + return types.ErrEmptyMessage + } - k.triggerHeaderInserted(ctx, headerInfo) - // The tip has changed, we need to send events - if !currentTip.Eq(previousTip) { - if !currentTip.Eq(headerInfo) { - panic("The tip was updated but with a different header than the one provided") - } - // Get the highest common ancestor between the new tip and the old tip - // There are two cases: - // 1. The new tip extends the old tip - // - The highest common ancestor is the old tip - // - No need to send a roll-back event - // 2. There has been a chain re-org - // - Need to send a roll-back event - var hca *types.BTCHeaderInfo - if currentTip.HasParent(previousTip) { - hca = previousTip - } else { - hca = k.headersState(ctx).GetHighestCommonAncestor(previousTip, currentTip) - // chain re-org: trigger a roll-back event to the highest common ancestor - k.triggerRollBack(ctx, hca) - } - // Find the newly added headers to the main chain - addedToMainChain = k.headersState(ctx).GetInOrderAncestorsUntil(currentTip, hca) - // Iterate through the added headers and trigger a roll-forward event - for _, added := range addedToMainChain { - // tipHeight + 1 - len(addedToMainChain) -> height of the highest common ancestor - k.triggerRollForward(ctx, added) - } + blockHeaders := make([]*wire.BlockHeader, len(headers)) + for i, header := range headers { + blockHeaders[i] = header.ToBlockHeader() } - return nil + return k.insertHeaders(ctx, blockHeaders) } // BlockHeight returns the height of the provided header -func (k Keeper) BlockHeight(ctx sdk.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) { +func (k Keeper) BlockHeight(ctx context.Context, headerHash *bbn.BTCHeaderHashBytes) (uint64, error) { if headerHash == nil { return 0, types.ErrEmptyMessage } - return k.headersState(ctx).GetHeaderHeight(headerHash) + + headerInfo, err := k.headersState(ctx).GetHeaderByHash(headerHash) + + if err != nil { + return 0, err + } + + return headerInfo.Height, nil } -// MainChainDepth returns the depth of the header in the main chain or -1 if it does not exist in it -func (k Keeper) MainChainDepth(ctx sdk.Context, headerHashBytes *bbn.BTCHeaderHashBytes) (int64, error) { +// MainChainDepth returns the depth of the header in the main chain, or error if it does not exists +func (k Keeper) MainChainDepth(ctx context.Context, headerHashBytes *bbn.BTCHeaderHashBytes) (uint64, error) { if headerHashBytes == nil { - return -1, types.ErrEmptyMessage + return 0, types.ErrEmptyMessage } // Retrieve the header. If it does not exist, return an error headerInfo, err := k.headersState(ctx).GetHeaderByHash(headerHashBytes) if err != nil { - return -1, err + return 0, err } - // Retrieve the tip tipInfo := k.headersState(ctx).GetTip() - // If the height of the requested header is larger than the tip, return -1 + // sanity check, to avoid silent error if something is wrong. if tipInfo.Height < headerInfo.Height { - return -1, nil + // panic, as tip should always be higher than the header than every header + panic("tip height is less than header height") } - // The depth is the number of blocks that have been build on top of the header - // For example: - // Tip: 0-deep - // Tip height is 10, headerInfo height is 5: 5-deep etc. headerDepth := tipInfo.Height - headerInfo.Height - mainchain := k.headersState(ctx).GetMainChainUpTo(headerDepth) - - // If we got an empty mainchain or the header does not equal the last element of the mainchain - // then the header is not maintained inside the mainchain. - if len(mainchain) == 0 || !headerInfo.Eq(mainchain[len(mainchain)-1]) { - return -1, nil - } - return int64(headerDepth), nil -} - -// IsHeaderKDeep returns true if a header is at least k-deep on the main chain -func (k Keeper) IsHeaderKDeep(ctx sdk.Context, headerHashBytes *bbn.BTCHeaderHashBytes, depth uint64) (bool, error) { - if headerHashBytes == nil { - return false, types.ErrEmptyMessage - } - mainchainDepth, err := k.MainChainDepth(ctx, headerHashBytes) - if err != nil { - return false, err - } - // If MainChainDepth returned a negative depth, then the header is not on the mainchain - if mainchainDepth < 0 { - return false, nil - } - // return true if the provided depth is more than equal the mainchain depth - return depth >= uint64(mainchainDepth), nil + return headerDepth, nil } -// IsAncestor returns true/false depending on whether `parent` is an ancestor of `child`. -// Returns false if the parent and the child are the same header. -func (k Keeper) IsAncestor(ctx sdk.Context, parentHashBytes *bbn.BTCHeaderHashBytes, childHashBytes *bbn.BTCHeaderHashBytes) (bool, error) { - // nil checks - if parentHashBytes == nil || childHashBytes == nil { - return false, types.ErrEmptyMessage - } - // Retrieve parent and child header - parentHeader, err := k.headersState(ctx).GetHeaderByHash(parentHashBytes) - if err != nil { - return false, types.ErrHeaderDoesNotExist.Wrapf("parent does not exist") - } - childHeader, err := k.headersState(ctx).GetHeaderByHash(childHashBytes) - if err != nil { - return false, types.ErrHeaderDoesNotExist.Wrapf("child does not exist") - } - - // If the height of the child is equal or less than the parent, then the input is invalid - if childHeader.Height <= parentHeader.Height { - return false, nil - } - - // Retrieve the ancestry - ancestry := k.headersState(ctx).GetHeaderAncestryUpTo(childHeader, childHeader.Height-parentHeader.Height) - // If it is empty, return false - if len(ancestry) == 0 { - return false, nil - } - // Return whether the last element of the ancestry is equal to the parent - return ancestry[len(ancestry)-1].Eq(parentHeader), nil -} - -func (k Keeper) GetTipInfo(ctx sdk.Context) *types.BTCHeaderInfo { +func (k Keeper) GetTipInfo(ctx context.Context) *types.BTCHeaderInfo { return k.headersState(ctx).GetTip() } -// TODO: The following functions, GetHeaderByHash and GetHeaderByHeight, are super inefficient -// and should be replaced with a better implementation. This requires changing the -// underlying data model for the whole btclightclient module. -// GetHeaderByHash returns header with given hash from main chain or returns nil if such header is not found -// or is not on main chain -func (k Keeper) GetHeaderByHash(ctx sdk.Context, hash *bbn.BTCHeaderHashBytes) *types.BTCHeaderInfo { - depth, err := k.MainChainDepth(ctx, hash) - - if depth < 0 || err != nil { - return nil - } - +// GetHeaderByHash returns header with given hash, if it does not exists returns nil +func (k Keeper) GetHeaderByHash(ctx context.Context, hash *bbn.BTCHeaderHashBytes) *types.BTCHeaderInfo { info, err := k.headersState(ctx).GetHeaderByHash(hash) if err != nil { @@ -255,36 +161,62 @@ func (k Keeper) GetHeaderByHash(ctx sdk.Context, hash *bbn.BTCHeaderHashBytes) * } // GetHeaderByHeight returns header with given height from main chain, returns nil if such header is not found -func (k Keeper) GetHeaderByHeight(ctx sdk.Context, height uint64) *types.BTCHeaderInfo { - var info *types.BTCHeaderInfo +func (k Keeper) GetHeaderByHeight(ctx context.Context, height uint64) *types.BTCHeaderInfo { + header, err := k.headersState(ctx).GetHeaderByHeight(height) + + if err != nil { + return nil + } + + return header +} - k.headersState(ctx).HeadersByHeight(height, func(hi *types.BTCHeaderInfo) bool { - depth, err := k.MainChainDepth(ctx, hi.Hash) +// GetMainChainFrom returns the current canonical chain from the given height up to the tip +// If the height is higher than the tip, it returns an empty slice +// If startHeight is 0, it returns the entire main chain +func (k Keeper) GetMainChainFrom(ctx context.Context, startHeight uint64) []*types.BTCHeaderInfo { + headers := make([]*types.BTCHeaderInfo, 0) + accHeaderFn := func(header *types.BTCHeaderInfo) bool { + headers = append(headers, header) + return false + } + k.headersState(ctx).IterateForwardHeaders(startHeight, accHeaderFn) + return headers +} - if depth < 0 || err != nil { - return false +// 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 context.Context, depth uint64) []*types.BTCHeaderInfo { + headers := make([]*types.BTCHeaderInfo, 0) + + var currentDepth = uint64(0) + accHeaderFn := func(header *types.BTCHeaderInfo) bool { + // header header is at depth 0. + if currentDepth > depth { + return true } - info = hi - return true - }) + headers = append(headers, header) + currentDepth++ + return false + } - return info -} + k.headersState(ctx).IterateReverseHeaders(accHeaderFn) -// 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) + return headers } -// 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) +// GetMainChainReverse Retrieves whole header chain in reverse order +func (k Keeper) GetMainChainReverse(ctx context.Context) []*types.BTCHeaderInfo { + headers := make([]*types.BTCHeaderInfo, 0) + accHeaderFn := func(header *types.BTCHeaderInfo) bool { + headers = append(headers, header) + return false + } + k.headersState(ctx).IterateReverseHeaders(accHeaderFn) + return headers } -// 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) +func (k Keeper) GetBTCNet() *chaincfg.Params { + return k.btcConfig.NetParams() } diff --git a/x/btclightclient/keeper/keeper_test.go b/x/btclightclient/keeper/keeper_test.go index b510be086..df9d6bb53 100644 --- a/x/btclightclient/keeper/keeper_test.go +++ b/x/btclightclient/keeper/keeper_test.go @@ -1,103 +1,31 @@ package keeper_test import ( + "errors" "math/rand" "testing" + sdkmath "cosmossdk.io/math" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/babylonchain/babylon/testutil/datagen" testkeeper "github.com/babylonchain/babylon/testutil/keeper" - "github.com/babylonchain/babylon/x/btclightclient/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) -func FuzzKeeperIsHeaderKDeep(f *testing.F) { - /* - Checks: - 1. if the BTCHeaderBytes object is nil, an error is returned - 2. if the header does not exist, an error is returned - 3. if the header exists but it is higher than `depth`, false is returned - 4. if the header exists and is equal to `depth`, true is returned - 5. if the header exists and is higher than `depth`, true is returned - 6. if the header exists and is equal or higher to `depth` but not on the main chain, false is returned - - Data Generation: - - Generate a random tree of headers. - - Get the mainchain and select appropriate headers. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - depth := r.Uint64() - - // Test nil input - isDeep, err := blcKeeper.IsHeaderKDeep(ctx, nil, depth) - if err == nil { - t.Errorf("Nil input led to nil error") - } - if isDeep { - t.Errorf("Nil input led to a true result") - } - - // Test header not existing - nonExistentHeader := datagen.GenRandomBTCHeaderBytes(r, nil, nil) - isDeep, err = blcKeeper.IsHeaderKDeep(ctx, nonExistentHeader.Hash(), depth) - if err == nil { - t.Errorf("Non existent header led to nil error") - } - if isDeep { - t.Errorf("Non existent header led to a true result") - } - - // Generate a random tree of headers with at least one node - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - // Get a random header from the tree - header := tree.RandomNode(r) - // Get the tip of the chain and check whether the header is on the chain that it defines - // In that case, the true/false result depends on the depth parameter that we provide. - // Otherwise, the result should always be false, regardless of the parameter. - tip := tree.GetTip() - if tree.IsOnNodeChain(tip, header) { - mainchain := tree.GetMainChain() - // Select a random depth based on the main-chain length - randDepth := uint64(r.Int63n(int64(len(mainchain)))) - isDeep, err = blcKeeper.IsHeaderKDeep(ctx, header.Hash, randDepth) - // Identify whether the function should return true or false - headerDepth := tip.Height - header.Height - // If the random depth that we chose is more than the headerDepth, then it should return true - expectedIsDeep := randDepth >= headerDepth - if err != nil { - t.Errorf("Existent header led to a non-nil error") - } - if expectedIsDeep != isDeep { - t.Errorf("Expected result %t for header with depth %d when parameter depth is %d", expectedIsDeep, headerDepth, randDepth) - } - } else { - // The depth provided does not matter, we should always get false. - randDepth := r.Uint64() - isDeep, err = blcKeeper.IsHeaderKDeep(ctx, header.Hash, randDepth) - if err != nil { - t.Errorf("Existent header led to a non-nil error %s", err) - } - if isDeep { - t.Errorf("Got a true result for header that is not part of the mainchain") - } - } - }) -} - func FuzzKeeperMainChainDepth(f *testing.F) { /* Checks: 1. if the BTCHeaderBytes object is nil, an error is returned and the height is -1 2. if the BTCHeaderBytes object does not exist in storage, (-1, error) is returned - 3. if the BTCHeaderBytes object has a height that is higher than the tip, (-1, error) is returned - 4. if the header is not on the main chain, (-1, nil) is returned - 5. if the header exists and is on the mainchain, (depth, nil) is returned + 3. if the header is not on the main chain, (0, nil) is returned + 4. if the header exists and is on the mainchain, (depth, nil) is returned Data Generation: - - Generate a random tree of headers. + - Generate a random chain of headers. - Random generation of a header that is not inserted into storage. - Random selection of a header from the main chain and outside of it. */ @@ -111,7 +39,7 @@ func FuzzKeeperMainChainDepth(f *testing.F) { if err == nil { t.Errorf("Nil input led to nil error") } - if depth != -1 { + if depth != 0 { t.Errorf("Nil input led to a result that is not -1") } @@ -121,36 +49,24 @@ func FuzzKeeperMainChainDepth(f *testing.F) { if err == nil { t.Errorf("Non existent header led to nil error") } - if depth != -1 { + if depth != 0 { t.Errorf("Non existing header led to a result that is not -1") } - // Generate a random tree of headers with at least one node - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - // Get a random header from the tree - header := tree.RandomNode(r) - // Get the tip of the chain and check whether the header is on the chain that it defines - // In that case, the depth result depends on the depth of the header on the mainchain. - // Otherwise, the result should always be -1 - tip := tree.GetTip() - // Get the depth - depth, err = blcKeeper.MainChainDepth(ctx, header.Hash) - if err != nil { - t.Errorf("Existent and header led to error") - } - if tree.IsOnNodeChain(tip, header) { - expectedDepth := tip.Height - header.Height - if depth < 0 { - t.Errorf("Mainchain header led to negative depth") - } - if uint64(depth) != expectedDepth { - t.Errorf("Got depth %d, expected %d", depth, expectedDepth) - } - } else { - if depth >= 0 { - t.Errorf("Non-mainchain header let to >= 0 result") - } - } + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+10, + ) + randomHeader := chain.GetRandomHeaderInfo(r) + depth, err = blcKeeper.MainChainDepth(ctx, randomHeader.Hash) + require.NoError(t, err) + chainTip := chain.GetTipInfo() + headerDepth := chainTip.Height - randomHeader.Height + require.Equal(t, headerDepth, depth) }) } @@ -162,7 +78,7 @@ func FuzzKeeperBlockHeight(f *testing.F) { 3. if the BTCHeaderBytes object exists, (height, nil) is returned. Data Generation: - - Generate a random tree of headers. + - Generate a random chain of headers. - Random generation of a header that is not inserted into storage. - Random selection of a header from the main chain and outside of it. */ @@ -190,8 +106,16 @@ func FuzzKeeperBlockHeight(f *testing.F) { t.Errorf("Non existing header led to a result that is not -1") } - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - header := tree.RandomNode(r) + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+10, + ) + + header := chain.GetRandomHeaderInfo(r) height, err = blcKeeper.BlockHeight(ctx, header.Hash) if err != nil { t.Errorf("Existent header led to an error") @@ -202,289 +126,334 @@ func FuzzKeeperBlockHeight(f *testing.F) { }) } -func FuzzKeeperIsAncestor(f *testing.F) { - /* - Checks: - 1. If the child hash or the parent hash are nil, an error is returned - 2. If the child has a lower height than the parent, an error is returned - 3. If the child and the parent are the same, false is returned - 4. If the parent is an ancestor of child then `true` is returned. - - Data generation: - - Generate a random tree of headers and insert it into storage. - - Select a random header and select a random descendant and a random ancestor to test (2-4). - */ +func FuzzKeeperInsertValidChainExtension(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - nonExistentParent := datagen.GenRandomBTCHeaderInfo(r) - nonExistentChild := datagen.GenRandomBTCHeaderInfo(r) + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + datagen.RandomInt(r, 50)+10, + datagen.RandomInt(r, 50)+10, + ) - // nil inputs test - isAncestor, err := blcKeeper.IsAncestor(ctx, nil, nil) - if err == nil { - t.Errorf("Nil input led to nil error") - } - if isAncestor { - t.Errorf("Nil input led to true result") - } - isAncestor, err = blcKeeper.IsAncestor(ctx, nonExistentParent.Hash, nil) - if err == nil { - t.Errorf("Nil input led to nil error") - } - if isAncestor { - t.Errorf("Nil input led to true result") - } - isAncestor, err = blcKeeper.IsAncestor(ctx, nil, nonExistentChild.Hash) - if err == nil { - t.Errorf("Nil input led to nil error") - } - if isAncestor { - t.Errorf("Nil input led to true result") - } + mockHooks := NewMockHooks() + blcKeeper.SetHooks(mockHooks) - // non-existent test - isAncestor, err = blcKeeper.IsAncestor(ctx, nonExistentParent.Hash, nonExistentChild.Hash) - if err == nil { - t.Errorf("Non existent headers led to nil error") - } - if isAncestor { - t.Errorf("Non existent headers led to true result") - } + newChainLength := uint32(datagen.RandomInt(r, 10) + 1) - // Generate random tree of headers - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - header := tree.RandomNode(r) - ancestor := tree.RandomNode(r) + chainToInsert := datagen.GenRandomValidChainStartingFrom( + r, + chain.GetTipInfo().Height, + chain.GetTipInfo().Header.ToBlockHeader(), + nil, + newChainLength, + ) + chainExtensionWork := chainWork(chainToInsert) - if ancestor.Eq(header) { - // Same headers test - isAncestor, err = blcKeeper.IsAncestor(ctx, ancestor.Hash, header.Hash) - if err != nil { - t.Errorf("Valid input led to an error") - } - if isAncestor { - t.Errorf("Same header input led to true result") - } - } else if ancestor.Height >= header.Height { // Descendant test - isAncestor, err = blcKeeper.IsAncestor(ctx, ancestor.Hash, header.Hash) - if err != nil { - t.Errorf("Providing a descendant as a parent led to a non-nil error") - } - if isAncestor { - t.Errorf("Providing a descendant as a parent led to a true result") - } - } else { // Ancestor test - isAncestor, err = blcKeeper.IsAncestor(ctx, ancestor.Hash, header.Hash) - if err != nil { - t.Errorf("Valid input led to an error") - } - if isAncestor != tree.IsOnNodeChain(header, ancestor) { // The result should be whether it is an ancestor or not - t.Errorf("Got invalid ancestry result. Expected %t, got %t", tree.IsOnNodeChain(header, ancestor), isAncestor) - } + ctx = ctx.WithEventManager(sdk.NewEventManager()) + oldTip := blcKeeper.HeadersState(ctx).GetTip() + extendedChainWork := oldTip.Work.Add(*chainExtensionWork) + extendedChainHeight := uint64(uint32(oldTip.Height) + newChainLength) + + err := blcKeeper.InsertHeaders(ctx, chainToChainBytes(chainToInsert)) + require.NoError(t, err) + + // updated tip + newTip := blcKeeper.HeadersState(ctx).GetTip() + require.False(t, newTip.Eq(oldTip)) + // check tip + checkTip( + t, + ctx, + blcKeeper, + extendedChainWork, + extendedChainHeight, + chainToInsert[len(chainToInsert)-1], + ) + // check all inserted headers + for _, header := range chainToInsert { + headerHash := header.BlockHash() + hash := bbn.NewBTCHeaderHashBytesFromChainhash(&headerHash) + headerInfoByHash := blcKeeper.GetHeaderByHash(ctx, &hash) + require.NotNil(t, headerInfoByHash) + headerInfoByHeight := blcKeeper.GetHeaderByHeight(ctx, headerInfoByHash.Height) + require.NotNil(t, headerInfoByHeight) + require.True(t, allFieldsEqual(headerInfoByHash, headerInfoByHeight)) + } + + // check events and hooks + rollForwadType, _ := sdk.TypedEventToEvent(&types.EventBTCRollForward{}) + headerInsertedType, _ := sdk.TypedEventToEvent(&types.EventBTCHeaderInserted{}) + + events := ctx.EventManager().Events() + numEvents := len(events) + require.Len(t, mockHooks.AfterBTCHeaderInsertedStore, len(chainToInsert)) + require.Len(t, mockHooks.AfterBTCRollForwardStore, len(chainToInsert)) + require.Len(t, mockHooks.AfterBTCRollBackStore, 0) + require.Equal(t, numEvents, len(chainToInsert)*2) + + for i, header := range chainToInsert { + headerHash := header.BlockHash() + hash := bbn.NewBTCHeaderHashBytesFromChainhash(&headerHash) + require.True(t, mockHooks.AfterBTCHeaderInsertedStore[i].Hash.Eq(&hash)) + require.True(t, mockHooks.AfterBTCRollForwardStore[i].Hash.Eq(&hash)) + // event should be in order inserted -> roll forward + require.Equal(t, events[i*2].Type, headerInsertedType.Type) + require.Equal(t, events[i*2+1].Type, rollForwadType.Type) } }) } -func FuzzKeeperInsertHeader(f *testing.F) { - /* - Checks: - 1. if the BTCHeaderBytes object is nil, an error is returned - 2. if the BTCHeaderBytes object corresponds to an existing header, an error is returned - 3. if the BTCHeaderBytes object parent is not maintained, an error is returned - 4. if all the checks pass: - 4a. corresponding objects have been created on the headers, hashToHeight, and hashToWork storages - 4b. the cumulative work of the added header is its own + its parent's - 4c. the height of the added header is its parent's + 1 - 4d. the object added to the headers storage corresponds to a header info with the above attributes - 4e. the tip is properly updated and corresponding roll-forward and roll-backward events are triggered. Three cases: - 4e1. The new header builds on top of the existing tip - - New header becomes the tip - - Roll-Forward event to the new tip - 4e2. The new header builds on a fork and the cumulative work is less than the tip - - The tip does not change - - No events are triggered - 4e3. The new header builds on a fork and the cumulative work is more than the tip - - New header becomes the tip - - Roll-backward event to the highest common ancestor (can use the `GetHighestCommonAncestor` function) - - Roll-forward event to all the elements of the fork after the highest common ancestor - - Data Generation: - - Generate a random tree of headers and insert them into storage. - - Construct BTCHeaderBytes object that corresponds to existing header - - Construct BTCHeaderBytes object for which its parent is not maintained - - Construct BTCHeaderBytes objects that: - * Build on top of the tip - * Build on top of a header that is `rand.Intn(tipHeight)` headers back from the tip. - - This should emulate both 4e2 and 4e3. - */ +func FuzzKeeperInsertValidBetterChain(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - - // Test nil input - err := blcKeeper.InsertHeader(ctx, nil) - if err == nil { - t.Errorf("Nil input led to nil error") - } + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + datagen.RandomInt(r, 50)+10, + datagen.RandomInt(r, 50)+10, + ) - existingHeader := tree.RandomNode(r) - err = blcKeeper.InsertHeader(ctx, existingHeader.Header) - if err == nil { - t.Errorf("Existing header led to nil error") - } - - nonExistentHeader := datagen.GenRandomBTCHeaderInfo(r) - err = blcKeeper.InsertHeader(ctx, nonExistentHeader.Header) - if err == nil { - t.Errorf("Header with non-existent parent led to nil error") - } - - // Create mock hooks that just store with what they were called mockHooks := NewMockHooks() blcKeeper.SetHooks(mockHooks) - // Select a random header and build a header on top of it - parentHeader := tree.RandomNode(r) - header := datagen.GenRandomBTCHeaderInfoWithParent(r, parentHeader) - - // Assign a new event manager - // We do this because the tree building might have led to events getting sent - // and we want to ignore those. + forkHeaderParent := chain.GetRandomHeaderInfoNoTip(r) + // new chain will always be better that existing one + newChainLength := uint32(chain.ChainLength() + 1) + chainToInsert := datagen.GenRandomValidChainStartingFrom( + r, + forkHeaderParent.Height, + forkHeaderParent.Header.ToBlockHeader(), + nil, + newChainLength, + ) + chainExtensionWork := chainWork(chainToInsert) ctx = ctx.WithEventManager(sdk.NewEventManager()) - // Get the tip in order to check if the header build on top of the tip + extendedChainWork := forkHeaderParent.Work.Add(*chainExtensionWork) + extendedChainHeight := uint64(uint32(forkHeaderParent.Height) + newChainLength) + oldTip := blcKeeper.HeadersState(ctx).GetTip() + removedBranch := blcKeeper.GetMainChainFrom(ctx, forkHeaderParent.Height+1) - // Insert the header into storage - err = blcKeeper.InsertHeader(ctx, header.Header) - if err != nil { - t.Errorf("Valid header led to an error") - } + require.True(t, len(removedBranch) > 0) - // Get the new tip - newTip := blcKeeper.HeadersState(ctx).GetTip() + err := blcKeeper.InsertHeaders(ctx, chainToChainBytes(chainToInsert)) + require.NoError(t, err) - // Get event types. Those will be useful to test the types of the emitted events - rollForwadType, _ := sdk.TypedEventToEvent(&types.EventBTCRollForward{}) + // updated tip + newTip := blcKeeper.HeadersState(ctx).GetTip() + require.False(t, newTip.Eq(oldTip)) + // check tip + checkTip( + t, + ctx, + blcKeeper, + extendedChainWork, + extendedChainHeight, + chainToInsert[len(chainToInsert)-1], + ) + + // check all headers from removed branch were removed + for _, headerInfo := range removedBranch { + headerInfoByHash := blcKeeper.GetHeaderByHash(ctx, headerInfo.Hash) + require.Nil(t, headerInfoByHash) + } + + // check all inserted headers + for _, header := range chainToInsert { + headerHash := header.BlockHash() + hash := bbn.NewBTCHeaderHashBytesFromChainhash(&headerHash) + headerInfoByHash := blcKeeper.GetHeaderByHash(ctx, &hash) + require.NotNil(t, headerInfoByHash) + headerInfoByHeight := blcKeeper.GetHeaderByHeight(ctx, headerInfoByHash.Height) + require.NotNil(t, headerInfoByHeight) + require.True(t, allFieldsEqual(headerInfoByHash, headerInfoByHeight)) + } + + // check events and hooks rollBackType, _ := sdk.TypedEventToEvent(&types.EventBTCRollBack{}) + rollForwadType, _ := sdk.TypedEventToEvent(&types.EventBTCRollForward{}) headerInsertedType, _ := sdk.TypedEventToEvent(&types.EventBTCHeaderInserted{}) - // The headerInserted hook call should contain the new header - if len(mockHooks.AfterBTCHeaderInsertedStore) != 1 { - t.Fatalf("Expected a single BTCHeaderInserted hook to be invoked. Got %d", len(mockHooks.AfterBTCHeaderInsertedStore)) - } - if !mockHooks.AfterBTCHeaderInsertedStore[0].Eq(header) { - t.Errorf("The headerInfo inside the BTCHeaderInserted hook is not the new header") - } - // Check that an event has been triggered for the new header - if len(ctx.EventManager().Events()) == 0 { - t.Fatalf("No events were triggered") - } + events := ctx.EventManager().Events() + numEvents := len(events) + require.Len(t, mockHooks.AfterBTCHeaderInsertedStore, len(chainToInsert)) + require.Len(t, mockHooks.AfterBTCRollForwardStore, len(chainToInsert)) + // there is one roll back event + require.Len(t, mockHooks.AfterBTCRollBackStore, 1) + require.Equal(t, numEvents, len(chainToInsert)*2+1) + + // Events should be ordered: + // Rollback, Insert, RollForward, Insert, RollForward, ... + for i, header := range chainToInsert { + if i == 0 { + // rollback event + require.Equal(t, events[0].Type, rollBackType.Type) + // rollback should indicate highest common ancestor i.e fork header parent + require.True(t, mockHooks.AfterBTCRollBackStore[0].Hash.Eq(forkHeaderParent.Hash)) + continue + } + + headerHash := header.BlockHash() + hash := bbn.NewBTCHeaderHashBytesFromChainhash(&headerHash) + require.True(t, mockHooks.AfterBTCHeaderInsertedStore[i].Hash.Eq(&hash)) + require.True(t, mockHooks.AfterBTCRollForwardStore[i].Hash.Eq(&hash)) - // The header creation event should have been the one that was first generated - if ctx.EventManager().Events()[0].Type != headerInsertedType.Type { - t.Errorf("The first event does not have the BTCHeaderInserted type") + require.Equal(t, events[i*2+1].Type, headerInsertedType.Type) + require.Equal(t, events[i*2].Type, rollForwadType.Type) } + }) +} - // If the new header builds on top of the tip - if oldTip.Eq(parentHeader) { - // The new tip should be equal to the new header - if !newTip.Eq(header) { - t.Errorf("Inserted header builts on top of the previous tip but does not become the new tip") - } - // The rollforward hook must be sent once - if len(mockHooks.AfterBTCRollForwardStore) != 1 { - t.Fatalf("Expected a single BTCRollForward hook to be invoked. Got %d", len(mockHooks.AfterBTCRollForwardStore)) - } - // The rollfoward hook call should contain the new header - if !mockHooks.AfterBTCRollForwardStore[0].Eq(header) { - t.Errorf("The headerInfo inside the BTCRollForward hook is not the new header") - } - // No rollback hooks must be invoked - if len(mockHooks.AfterBTCRollBackStore) != 0 { - t.Fatalf("Expected the BTCRollBack hook to not be invoked") - } - // 2 events because the first one is for the header creation - if len(ctx.EventManager().Events()) != 2 { - t.Fatalf("We expected only two events. One for header creation and one for rolling forward.") - } - // The second event should be the roll forward one - if ctx.EventManager().Events()[1].Type != rollForwadType.Type { - t.Errorf("The second event does not have the roll forward type") - } - } else if oldTip.Work.GT(*header.Work) { - // If the tip has a greater work than the newly inserted header - // no events should be sent and the tip should not change - if !oldTip.Eq(newTip) { - t.Errorf("Header with less work inserted but the tip changed") - } - // No rollforward hooks should be invoked - if len(mockHooks.AfterBTCRollForwardStore) != 0 { - t.Fatalf("Expected the BTCRollForward hook to not be invoked") - } - // No rollback hooks should be invoked - if len(mockHooks.AfterBTCRollBackStore) != 0 { - t.Fatalf("Expected the BTCRollBack hook to not be invoked") - } - // No other events other than BTCHeaderInserted should be invoked - if len(ctx.EventManager().Events()) != 1 { - t.Errorf("Extra events have been invoked when the tip wasn't updated") - } - } else { - // The tip has been updated. It should be towards the new header - if !newTip.Eq(header) { - t.Errorf("Inserted header has more work than the previous tip but does not become the new tip") - } - // Get the highest common ancestor of the old tip and the new header - hca := blcKeeper.HeadersState(ctx).GetHighestCommonAncestor(header, oldTip) - // Get the ancestry of the header up to the highest common ancestor - ancestry := tree.GetNodeAncestryUpTo(header, hca) - // We should have as many invocations of the roll-forward hook as the ancestors - // up to the highest common one - if len(ancestry) != len(mockHooks.AfterBTCRollForwardStore) { - t.Fatalf("Expected as many invocations of the roll-forward hook as the number of ancestors.") - } - for i := 0; i < len(ancestry); i++ { - // Compare the nodes in reverse order, since the rollfoward events should be in an oldest header first manner. - if !ancestry[i].Eq(mockHooks.AfterBTCRollForwardStore[len(ancestry)-i-1]) { - t.Errorf("Headers do not match. Expected %s got %s", ancestry[i].Hash, mockHooks.AfterBTCRollForwardStore[len(ancestry)-i-1].Hash) - } - } +func FuzzKeeperInsertInvalidChain(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) + _, _ = genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + datagen.RandomInt(r, 50)+10, + ) + currentTip := blcKeeper.GetTipInfo(ctx) + require.NotNil(t, currentTip) + + // Inserting nil headers should result with error + errNil := blcKeeper.InsertHeaders(ctx, nil) + require.Error(t, errNil) + + // Inserting empty headers should result with error + errEmpty := blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{}) + require.Error(t, errEmpty) + + // Inserting header without existing parent should result with error + chain := datagen.NewBTCHeaderChainWithLength(r, 0, 0, 10) + errNoParent := blcKeeper.InsertHeaders(ctx, chain.ChainToBytes()[1:]) + require.Error(t, errNoParent) + require.True(t, errors.Is(errNoParent, types.ErrHeaderParentDoesNotExist)) + + // Inserting header chain with invalid header should result in error + newChainLength := uint32(datagen.RandomInt(r, 10) + 5) + // valid chain with at least 5 headers + chainToInsert := datagen.GenRandomValidChainStartingFrom( + r, + chain.GetTipInfo().Height, + chain.GetTipInfo().Header.ToBlockHeader(), + nil, + newChainLength, + ) + + // bump the nonce, it should fail validation and tip should not change + chainToInsert[3].Nonce = chainToInsert[3].Nonce + 1 + errInvalidHeader := blcKeeper.InsertHeaders(ctx, chainToChainBytes(chainToInsert)) + require.Error(t, errInvalidHeader) + newTip := blcKeeper.GetTipInfo(ctx) + // tip did not change + require.True(t, allFieldsEqual(currentTip, newTip)) + + // Inserting header chain with less work than current chain work should result in error + headerBeforeTip := blcKeeper.GetHeaderByHeight(ctx, currentTip.Height-1) + require.NotNil(t, headerBeforeTip) + worseChain := datagen.GenRandomValidChainStartingFrom( + r, + headerBeforeTip.Height, + headerBeforeTip.Header.ToBlockHeader(), + nil, + 1, + ) + errWorseChain := blcKeeper.InsertHeaders(ctx, chainToChainBytes(worseChain)) + require.Error(t, errWorseChain) + require.True(t, errors.Is(errWorseChain, types.ErrChainWithNotEnoughWork)) + }) +} - // The rollback hook should be invoked for the highest common ancestor - if len(mockHooks.AfterBTCRollBackStore) != 1 { - t.Fatalf("Expected the BTCRollBack hook to be invoked once") - } - if !mockHooks.AfterBTCRollBackStore[0].Eq(hca) { - t.Errorf("Expected the BTCRollBack hook to be invoked for the highest common ancestor") - } +func FuzzKeeperValdateHeaderAtDifficultyAdjustmentBoundaries(f *testing.F) { + // less seeds as we generate longer chains + datagen.AddRandomSeedsToFuzzer(f, 3) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + numBlockPerRetarget := types.BlocksPerRetarget(&chaincfg.SimNetParams) + blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - // Test the invoked events - invokedEvents := ctx.EventManager().Events() - // There should be a total of len(ancestry) + 2 events - if len(invokedEvents) != len(ancestry)+2 { - t.Errorf("More events than expected were invoked %d %d", len(invokedEvents), len(ancestry)+2) - } - // Only test that there is a certain number of rollForward and rollBack events - // Testing the attributes is a much more complex approach - rollForwardCnt := 0 - rollBackCnt := 0 - for i := 0; i < len(invokedEvents); i++ { - if invokedEvents[i].Type == rollForwadType.Type { - rollForwardCnt += 1 - } - if invokedEvents[i].Type == rollBackType.Type { - rollBackCnt += 1 - } - } - if rollForwardCnt != len(ancestry) || rollBackCnt != 1 { - t.Errorf("Wrong number of roll forward and roll back events") - } - } + genesisHeader := bbn.NewBTCHeaderBytesFromBlockHeader(&chaincfg.SimNetParams.GenesisBlock.Header) + genesisHash := bbn.NewBTCHeaderHashBytesFromChainhash(chaincfg.SimNetParams.GenesisHash) + genesisWork := sdkmath.NewUint(0) + + genesisInfo := types.NewBTCHeaderInfo( + &genesisHeader, + &genesisHash, + 0, + &genesisWork, + ) + + require.True(t, types.IsRetargetBlock(genesisInfo, &chaincfg.SimNetParams)) + blcKeeper.SetBaseBTCHeader(ctx, *genesisInfo) + randomChain := datagen.NewBTCHeaderChainFromParentInfo( + r, + genesisInfo, + uint32(numBlockPerRetarget), + ) + + // this will always fail as last header is at adjustment boundary, but we created + // it without adjustment + err := blcKeeper.InsertHeaders(ctx, randomChain.ChainToBytes()) + require.Error(t, err) + + randomChainWithoutLastHeader := randomChain.Headers[:len(randomChain.Headers)-1] + chain := chainToChainBytes(randomChainWithoutLastHeader) + // now all headers are valid, and we are below adjustment boundary + err = blcKeeper.InsertHeaders(ctx, chain) + require.NoError(t, err) + + currentTip := blcKeeper.GetTipInfo(ctx) + require.NotNil(t, currentTip) + require.Equal(t, currentTip.Height, uint64(numBlockPerRetarget)-1) + + invalidAdjustedHeader := datagen.GenRandomBtcdValidHeader( + r, + currentTip.Header.ToBlockHeader(), + nil, + nil, + ) + // try to insert header at adjustment boundary without adjustment should fail + err = blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(invalidAdjustedHeader)}) + require.Error(t, err) + + // Inserting valid adjusted header should succeed + rt := datagen.RetargetInfo{ + LastRetargetHeader: genesisHeader.ToBlockHeader(), + Params: &chaincfg.SimNetParams, + } + validAdjustedHeader := datagen.GenRandomBtcdValidHeader( + r, + // current tip heigh is 2015 + currentTip.Header.ToBlockHeader(), + nil, + &rt, + ) + validAdjustedHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(validAdjustedHeader) + + err = blcKeeper.InsertHeaders(ctx, []bbn.BTCHeaderBytes{bbn.NewBTCHeaderBytesFromBlockHeader(validAdjustedHeader)}) + require.NoError(t, err) + + newTip := blcKeeper.GetTipInfo(ctx) + require.NotNil(t, newTip) + // tip should be at adjustment boundary now + require.Equal(t, newTip.Height, uint64(numBlockPerRetarget)) + require.True(t, newTip.Header.Eq(&validAdjustedHeaderBytes)) + require.True(t, types.IsRetargetBlock(newTip, &chaincfg.SimNetParams)) }) } diff --git a/x/btclightclient/keeper/msg_server.go b/x/btclightclient/keeper/msg_server.go index 2956332f0..cd8fff632 100644 --- a/x/btclightclient/keeper/msg_server.go +++ b/x/btclightclient/keeper/msg_server.go @@ -2,13 +2,11 @@ package keeper import ( "context" - "math/big" + errorsmod "cosmossdk.io/errors" "github.com/babylonchain/babylon/x/btclightclient/types" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcd/btcutil" sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) type msgServer struct { @@ -16,156 +14,62 @@ type msgServer struct { k Keeper } -func MsgInsertHeaderWrapped(ctx context.Context, k Keeper, msg *types.MsgInsertHeader, - powLimit big.Int, reduceMinDifficulty bool, retargetAdjustmentFactor int64, powCheck bool, -) (*types.MsgInsertHeaderResponse, error) { - // Perform the checks that checkBlockHeaderContext of btcd does - // https://github.com/btcsuite/btcd/blob/master/blockchain/validate.go#L644 - // We skip the time, checkpoint, and version checks - // TODO: Implement an AnteHandler that performs these checks - // so as to not pollute the mempool with transactions - // that will get rejected. +func (m msgServer) canInsertHeaders(sdkCtx sdk.Context, reporterAddress sdk.AccAddress) bool { + params := m.k.GetParams(sdkCtx) + + if params.AllowAllReporters() { + return true + } + + var allowInsertHeaders bool = false + for _, addr := range params.InsertHeadersAllowList { + if sdk.MustAccAddressFromBech32(addr).Equals(reporterAddress) { + allowInsertHeaders = true + } + } + + return allowInsertHeaders +} + +func (m msgServer) InsertHeaders(ctx context.Context, msg *types.MsgInsertHeaders) (*types.MsgInsertHeadersResponse, error) { if msg == nil { return nil, types.ErrEmptyMessage.Wrapf("message is nil") } + sdkCtx := sdk.UnwrapSDKContext(ctx) - if msg.Header == nil { - return nil, types.ErrEmptyMessage.Wrapf("message header is nit") + if err := msg.ValidateStateless(); err != nil { + return nil, types.ErrInvalidMessageFormat.Wrapf("invalid insert header message: %v", err) } - // Get the SDK wrapped context - sdkCtx := sdk.UnwrapSDKContext(ctx) + reporterAddress := msg.ReporterAddress() + + if !m.canInsertHeaders(sdkCtx, reporterAddress) { + return nil, types.ErrUnauthorizedReporter.Wrapf("reporter %s is not authorized to insert headers", reporterAddress) + } + + err := m.k.InsertHeaders(sdkCtx, msg.Headers) - parentHash := msg.Header.ParentHash() - // Retrieve parent - parent, err := k.headersState(sdkCtx).GetHeaderByHash(parentHash) - // parent does not exist if err != nil { return nil, err } + return &types.MsgInsertHeadersResponse{}, nil +} - /* - Verify the work of the new header. - Bitcoin core does this verification at: - https://github.com/bitcoin/bitcoin/blob/a688ff9046a9df58a373086445ab5796cccf9dd3/src/validation.cpp#L3468 - This function is invoked to identify the value that the `Bits` field should have: - https://github.com/bitcoin/bitcoin/blob/a688ff9046a9df58a373086445ab5796cccf9dd3/src/pow.cpp#L13 - - **Goal** - The goal of this check is to avoid the flooding of the btclightclient with headers that are easy to generate. - We want to avoid adding very complex checks here since they can be a source of bugs. Therefore, - we are ok getting headers that could not be part of the canonical chain, as long as sufficient - work has been put to generate them. - - The algorithm works as follows: - Every `params.DifficultyAdjustmentInterval()` the required work of the header is subject to change - in order to help maintain a 10-minutes average time between blocks. - - The configuration contains a `params.ReduceMinDifficulty` parameter that allows headers - to have the minimum amount of work allowed by the network regardless of the previous header's work. - For the mainnet this is set to false, while for the testnet/simnet this is set to true. - - Note: despite the naming, `params.powLimit` refers to the *minimum* work that is allowed. - However, due to the format of the Bits field, when converting those to big ints, - the following check reveals whether the work is more than the minimum: - workBigInt < powLimitBigInt - - 1. If the new block is NOT the first header of the adjustment interval: - a. If `params.ReduceMinDifficulty` has been set - i. If the time of the new block is after 20 minutes from the last block (`params.nPowTargetSpacing*2`) - The expected work of the header is `params.powLimit`. - ii. Otherwise, - The expected work of the header is equal to one of its ancestors. - The ancestor is selected by traversing all ancestors in the given `params.DifficultyAdjustmentInterval()` - and selecting the first one that has either work that is more than the `params.powLimit` or the first one - in the interval. - b. Otherwise, - i. The expected work of the header is equal to the one of its parent. - - From the above and given our goal, a valid check for this case would be that the header - has work that is at least more than the `params.powLimit`. - We will not check whether the work of the new header is exactly the same as the one that is - expected. - - 2. Otherwise, - a. Get the first block of the `params.DifficultyAdjustmentInterval()` - b. Calculate the timespan between the last block and the first block of the interval. - c. Ensure that the expected work won't wildly fluctuate from the work of the parent: - i. If timespan < `params.nPowTargetTimespan / params.retargetAdjustmentFactor` - timespan = `params.nPowTargetTimespan / params.retargetAdjustmentFactor` - ii. if timespan > `params.nPowTargetTimespan * retarget.AdjustmentFactor` - timespan = `params.nPowTargetTimespan * params.retargetAdjustmentFactor` - - For both the mainnet and the testnet/simnet `params.retargetAdjustmentFactor = 4`. - d. Get PoW of the last block and calculate: - newPow = parentPow * timespan / `params.nPowTargetTimespan` - - From the above calculation and based on (c), we can get the property: - parentPow / `params.retargetAdjustmentFactor` <= newPow <= parentPow * `params.retargetAdjustmentFactor` - e. If newPow > `params.PowLimit` - newPow = `params.PowLimit` - - From the above and given our goal, a valid check for this case would be that: - i. The header has work that is at least more than the `params.powLimit` - ii. The header has work that is between the multiple and dividend of the parent work and `params.retargetAdjustmentFactor` - - Given the stated goal, we would like to reduce complexity as much as possible. Therefore, - here we have decided to not differentiate cases (1) and (2). More specifically, the checks that we do are: - 1. Always verify that the work is at least more than `params.powLimit`. - 2. In the case that `params.ReduceMinDifficulty` has been set to `false`, check - that the header has work that is between the multiple and dividend of the parent work and `params.retargetAdjustmentFactor` - This only happens on the mainnet. - - The above checks lead to some more clutter on the testnet, since some headers won't do check (2) while they should, - but the testnet already allows for minimum work headers so that can be tolerated. For the mainnet, - we will do all required checks, but we will still not test the exact value of the `Bits` field. - Instead we will verify that the `Bits` field value is going to be in a valid range and if someone - does BTC mainnet work to add clutter on the BBN chain, then we can tolerate that. - */ - - if powCheck { - msgBlock := &wire.MsgBlock{Header: *(msg.Header.ToBlockHeader())} - block := btcutil.NewBlock(msgBlock) - err = blockchain.CheckProofOfWork(block, &powLimit) - if err != nil { - return nil, types.ErrInvalidProofOfWOrk - } +func (ms msgServer) UpdateParams(ctx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if ms.k.authority != req.Authority { + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.k.authority, req.Authority) } - - if !reduceMinDifficulty { - // The new block will either be the first block of a recalculation event - // which happens every 2,016 blocks or a normal block. - // In the second case, it's difficulty should be exactly the same as it's parent - // while in the second case it should have a maximum difference of a factor of 4 from it - // See: https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch10.asciidoc#retargeting-to-adjust-difficulty - // We consolidate those into a single check. - oldDifficulty := blockchain.CompactToBig(parent.Header.Bits()) - currentDifficulty := blockchain.CompactToBig(msg.Header.Bits()) - maxCurrentDifficulty := new(big.Int).Mul(oldDifficulty, big.NewInt(retargetAdjustmentFactor)) - minCurrentDifficulty := new(big.Int).Div(oldDifficulty, big.NewInt(retargetAdjustmentFactor)) - if currentDifficulty.Cmp(maxCurrentDifficulty) > 0 || currentDifficulty.Cmp(minCurrentDifficulty) < 0 { - return nil, types.ErrInvalidDifficulty.Wrap("difficulty not relevant to parent difficulty") - } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) } - // All good, insert the header - err = k.InsertHeader(sdkCtx, msg.Header) - if err != nil { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + if err := ms.k.SetParams(sdkCtx, req.Params); err != nil { return nil, err } - return &types.MsgInsertHeaderResponse{}, nil -} -func (m msgServer) InsertHeader(ctx context.Context, msg *types.MsgInsertHeader) (*types.MsgInsertHeaderResponse, error) { - return MsgInsertHeaderWrapped( - ctx, - m.k, - msg, - m.k.btcConfig.PowLimit(), - m.k.btcConfig.ReduceMinDifficulty(), - m.k.btcConfig.RetargetAdjustmentFactor(), - true, - ) + return &types.MsgUpdateParamsResponse{}, nil } // NewMsgServerImpl returns an implementation of the MsgServer interface diff --git a/x/btclightclient/keeper/msg_server_test.go b/x/btclightclient/keeper/msg_server_test.go index b3fa85907..a3dacb251 100644 --- a/x/btclightclient/keeper/msg_server_test.go +++ b/x/btclightclient/keeper/msg_server_test.go @@ -2,131 +2,228 @@ package keeper_test import ( "context" - "math/big" "math/rand" "testing" + "time" - sdkmath "cosmossdk.io/math" "github.com/babylonchain/babylon/testutil/datagen" - "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" keepertest "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/x/btclightclient/keeper" "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" ) func setupMsgServer(t testing.TB) (types.MsgServer, *keeper.Keeper, context.Context) { k, ctx := keepertest.BTCLightClientKeeper(t) - return keeper.NewMsgServerImpl(*k), k, sdk.WrapSDKContext(ctx) + return keeper.NewMsgServerImpl(*k), k, ctx } -func FuzzMsgServerInsertHeader(f *testing.F) { - /* - Test that: - 1. if the input message is nil, (nil, error) is returned - 2. if the msg does not contain a header, (nil, error) is returned - 3. if the parent of the header does not exist, (nil, error) is returned - 4. if the work of the header is not within the limits of the new header, (nil, error) is returned - 5. if all checks pass, the header is inserted into storage and an (empty MsgInsertHeaderResponse, nil) is returned - - we do not need to perform insertion checks since those are performed on FuzzKeeperInsertHeader - Building: - - Construct a random tree and insert into storage - - Generate a random header for which its parent does not exist - - Select a random header from the tree and construct BTCHeaderBytes objects on top of it with different work - 1. 4 times the work of parent - 2. 1 < work < 4 times the work of parent - 3. work > 4 times the work of the parent - 4. parent 4 times the work of the header - 5. parent 1 < work < 4 times the work of the header - 6. parent > 4 times the work of the header - */ - datagen.AddRandomSeedsToFuzzer(f, 10) +func setupMsgServerWithCustomParams(t testing.TB, p types.Params) (types.MsgServer, *keeper.Keeper, context.Context) { + k, ctx := keepertest.BTCLightClientKeeperWithCustomParams(t, p) + return keeper.NewMsgServerImpl(*k), k, ctx +} + +// Property: Inserting valid chain which has current tip as parent, should always update the chain +// and tip +func FuzzMsgServerInsertNewTip(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 5) + senderPrivKey := secp256k1.GenPrivKey() + address, err := sdk.AccAddressFromHexUnsafe(senderPrivKey.PubKey().Address().String()) + require.NoError(f, err) + f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, blcKeeper, sdkCtx := setupMsgServer(t) - - defaultParams := chaincfg.MainNetParams - powLimit := defaultParams.PowLimit - reduceMinDifficulty := defaultParams.ReduceMinDifficulty - retargetAdjustmentFactor := defaultParams.RetargetAdjustmentFactor - - // If the input message is nil, (nil, error) is returned - var msg *types.MsgInsertHeader = nil - resp, err := keeper.MsgInsertHeaderWrapped(sdkCtx, *blcKeeper, msg, *powLimit, reduceMinDifficulty, - retargetAdjustmentFactor, false) - if resp != nil { - t.Errorf("Nil message returned a response") - } - if err == nil { - t.Errorf("Nil message did not return an error") - } - - // If the message does not contain a header, (nil, error) is returned. - msg = &types.MsgInsertHeader{} - resp, err = keeper.MsgInsertHeaderWrapped(sdkCtx, *blcKeeper, msg, *powLimit, reduceMinDifficulty, - retargetAdjustmentFactor, false) - if resp != nil { - t.Errorf("Message without a header returned a response") - } - if err == nil { - t.Errorf("Message without a header did not return an error") - } - - // If the header has a parent that does not exist, (nil, error) is returned - headerParentNotExists := datagen.GenRandomBTCHeaderInfo(r).Header - msg = &types.MsgInsertHeader{Header: headerParentNotExists} - resp, err = keeper.MsgInsertHeaderWrapped(sdkCtx, *blcKeeper, msg, *powLimit, reduceMinDifficulty, - retargetAdjustmentFactor, false) - if resp != nil { - t.Errorf("Message with header with non-existent parent returned a response") - } - if err == nil { - t.Errorf("Message with header with non-existent parent did not return an error") - } + srv, blcKeeper, sdkCtx := setupMsgServer(t) + ctx := sdk.UnwrapSDKContext(sdkCtx) + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + datagen.RandomInt(r, 50)+10, + datagen.RandomInt(r, 50)+10, + ) + initTip := chain.GetTipInfo() + + checkTip( + t, + ctx, + blcKeeper, + *initTip.Work, + initTip.Height, + initTip.Header.ToBlockHeader(), + ) + + chainExenstionLength := uint32(r.Int31n(200) + 1) + chainExtension := datagen.GenRandomValidChainStartingFrom( + r, + initTip.Height, + initTip.Header.ToBlockHeader(), + nil, + chainExenstionLength, + ) + chainExtensionWork := chainWork(chainExtension) + + msg := &types.MsgInsertHeaders{Signer: address.String(), Headers: chainToChainBytes(chainExtension)} + + _, err := srv.InsertHeaders(sdkCtx, msg) + require.NoError(t, err) + + extendedChainWork := initTip.Work.Add(*chainExtensionWork) + extendedChainHeight := uint64(uint32(initTip.Height) + chainExenstionLength) + checkTip( + t, + ctx, + blcKeeper, + extendedChainWork, + extendedChainHeight, + chainExtension[len(chainExtension)-1], + ) + }) +} + +// Property: Inserting valid better chain should always update the chain and tip +func FuzzMsgServerReorgChain(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 5) + senderPrivKey := secp256k1.GenPrivKey() + address, err := sdk.AccAddressFromHexUnsafe(senderPrivKey.PubKey().Address().String()) + require.NoError(f, err) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + srv, blcKeeper, sdkCtx := setupMsgServer(t) ctx := sdk.UnwrapSDKContext(sdkCtx) - // Construct a tree and insert it into storage - tree := genRandomTree(r, blcKeeper, ctx, uint64(2), 10) - parentHeader := tree.RandomNode(r) - // Do not work with different cases. Select a random integer between 1-retargetAdjustmentFactor+1 - // 1/retargetAdjustmentFactor times, the work is going to be invalid - parentHeaderDifficulty := parentHeader.Header.Difficulty() - // Avoid retargetAdjustmentFactor itself, since the many conversions might lead to inconsistencies - mul := datagen.RandomInt(r, int(retargetAdjustmentFactor-1)) + 1 - if datagen.OneInN(r, 10) { // Give an invalid mul sometimes - mul = uint64(retargetAdjustmentFactor + 1) - } - headerDifficultyMul := sdkmath.NewUintFromBigInt(new(big.Int).Mul(parentHeaderDifficulty, big.NewInt(int64(mul)))) - headerDifficultyDiv := sdkmath.NewUintFromBigInt(new(big.Int).Div(parentHeaderDifficulty, big.NewInt(int64(mul)))) - - // Do tests - headerMoreWork := datagen.GenRandomBTCHeaderInfoWithParentAndBits(r, parentHeader, &headerDifficultyMul) - msg = &types.MsgInsertHeader{Header: headerMoreWork.Header} - resp, err = keeper.MsgInsertHeaderWrapped(sdkCtx, *blcKeeper, msg, *powLimit, reduceMinDifficulty, - retargetAdjustmentFactor, false) - if mul > uint64(retargetAdjustmentFactor) && resp != nil { - t.Errorf("Invalid header work led to a response getting returned") - } - if mul > uint64(retargetAdjustmentFactor) && err == nil { - t.Errorf("Invalid header work did not lead to an error %d %s %s %s", mul, headerDifficultyMul, headerDifficultyDiv, parentHeaderDifficulty) - } - if mul <= uint64(retargetAdjustmentFactor) && err != nil { - t.Errorf("Valid header work led to an error") - } - - headerLessWork := datagen.GenRandomBTCHeaderInfoWithParentAndBits(r, parentHeader, &headerDifficultyDiv) - msg = &types.MsgInsertHeader{Header: headerLessWork.Header} - resp, err = keeper.MsgInsertHeaderWrapped(sdkCtx, *blcKeeper, msg, *powLimit, reduceMinDifficulty, - retargetAdjustmentFactor, false) - if mul > uint64(retargetAdjustmentFactor) && resp != nil { - t.Errorf("Invalid header work led to a response getting returned") - } - if mul > uint64(retargetAdjustmentFactor) && err == nil { - t.Errorf("Invalid header work did not lead to an error") - } - if mul <= uint64(retargetAdjustmentFactor) && err != nil { - t.Errorf("Valid header work led to an error %d %s", mul, err) - } + + chainLength := datagen.RandomInt(r, 50) + 10 + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + datagen.RandomInt(r, 50)+10, + chainLength, + ) + initTip := chain.GetTipInfo() + + checkTip( + t, + ctx, + blcKeeper, + *initTip.Work, + initTip.Height, + initTip.Header.ToBlockHeader(), + ) + + reorgDepth := r.Intn(int(chainLength-1)) + 1 + + forkHeaderHeight := initTip.Height - uint64(reorgDepth) + forkHeader := blcKeeper.GetHeaderByHeight(ctx, forkHeaderHeight) + require.NotNil(t, forkHeader) + + // fork chain will always be longer that current c + forkChainLen := reorgDepth + 10 + chainExtension := datagen.GenRandomValidChainStartingFrom( + r, + forkHeader.Height, + forkHeader.Header.ToBlockHeader(), + nil, + uint32(forkChainLen), + ) + chainExtensionWork := chainWork(chainExtension) + msg := &types.MsgInsertHeaders{Signer: address.String(), Headers: chainToChainBytes(chainExtension)} + + _, err := srv.InsertHeaders(sdkCtx, msg) + require.NoError(t, err) + + extendedChainWork := forkHeader.Work.Add(*chainExtensionWork) + extendedChainHeight := forkHeader.Height + uint64(forkChainLen) + + checkTip( + t, + ctx, + blcKeeper, + extendedChainWork, + extendedChainHeight, + chainExtension[len(chainExtension)-1], + ) }) } + +func TestAllowUpdatesOnlyFromReportesInTheList(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sender1 := secp256k1.GenPrivKey() + address1, err := sdk.AccAddressFromHexUnsafe(sender1.PubKey().Address().String()) + require.NoError(t, err) + sender2 := secp256k1.GenPrivKey() + address2, err := sdk.AccAddressFromHexUnsafe(sender2.PubKey().Address().String()) + require.NoError(t, err) + sender3 := secp256k1.GenPrivKey() + address3, err := sdk.AccAddressFromHexUnsafe(sender3.PubKey().Address().String()) + require.NoError(t, err) + + params := types.NewParams( + // only sender1 and sender2 are allowed to update + []string{address1.String(), address2.String()}, + ) + + srv, blcKeeper, sdkCtx := setupMsgServerWithCustomParams(t, params) + ctx := sdk.UnwrapSDKContext(sdkCtx) + + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + 0, + 10, + ) + initTip := chain.GetTipInfo() + + checkTip( + t, + ctx, + blcKeeper, + *initTip.Work, + initTip.Height, + initTip.Header.ToBlockHeader(), + ) + + chainExtension := datagen.GenRandomValidChainStartingFrom( + r, + initTip.Height, + initTip.Header.ToBlockHeader(), + nil, + 10, + ) + + // sender 1 is allowed to update, it should succeed + msg := &types.MsgInsertHeaders{Signer: address1.String(), Headers: chainToChainBytes(chainExtension)} + _, err = srv.InsertHeaders(sdkCtx, msg) + require.NoError(t, err) + + newTip := blcKeeper.GetTipInfo(ctx) + require.NotNil(t, newTip) + + newChainExt := datagen.GenRandomValidChainStartingFrom( + r, + newTip.Height, + newTip.Header.ToBlockHeader(), + nil, + 10, + ) + + // sender 3 is not allowed to update, it should fail + msg1 := &types.MsgInsertHeaders{Signer: address3.String(), Headers: chainToChainBytes(newChainExt)} + _, err = srv.InsertHeaders(sdkCtx, msg1) + require.Error(t, err) + require.ErrorIs(t, err, types.ErrUnauthorizedReporter) + + // sender 2 is allowed to update, it should succeed + msg1 = &types.MsgInsertHeaders{Signer: address2.String(), Headers: chainToChainBytes(newChainExt)} + _, err = srv.InsertHeaders(sdkCtx, msg1) + require.NoError(t, err) +} diff --git a/x/btclightclient/keeper/params.go b/x/btclightclient/keeper/params.go new file mode 100644 index 000000000..bd68dd5d6 --- /dev/null +++ b/x/btclightclient/keeper/params.go @@ -0,0 +1,31 @@ +package keeper + +import ( + "context" + + "github.com/babylonchain/babylon/x/btclightclient/types" +) + +// SetParams sets the x/btclightclient module parameters. +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { + if err := p.Validate(); err != nil { + return err + } + store := k.storeService.OpenKVStore(ctx) + bz := k.cdc.MustMarshal(&p) + return store.Set(types.ParamsKey, bz) +} + +// GetParams returns the current x/btclightclient module parameters. +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } + if bz == nil { + return p + } + k.cdc.MustUnmarshal(bz, &p) + return p +} diff --git a/x/btclightclient/keeper/params_test.go b/x/btclightclient/keeper/params_test.go new file mode 100644 index 000000000..4dc68b70f --- /dev/null +++ b/x/btclightclient/keeper/params_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/stretchr/testify/require" +) + +func TestGetParams(t *testing.T) { + k, ctx := testkeeper.BTCLightClientKeeper(t) + // using nil as empty params list as, default proto decoder deserializes empty list as nil + params := types.NewParams(nil) + + err := k.SetParams(ctx, params) + require.NoError(t, err) + + retrievedParams := k.GetParams(ctx) + require.EqualValues(t, params, retrievedParams) +} diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 439d74356..27c869cea 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -1,324 +1,133 @@ package keeper import ( - sdkmath "cosmossdk.io/math" + "context" + "fmt" + + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/runtime" + + "cosmossdk.io/store/prefix" bbn "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) type headersState struct { cdc codec.BinaryCodec - headers sdk.KVStore - hashToHeight sdk.KVStore - hashToWork sdk.KVStore - tip sdk.KVStore + headers storetypes.KVStore + hashToHeight storetypes.KVStore } -func (k Keeper) headersState(ctx sdk.Context) headersState { +func (k Keeper) headersState(ctx context.Context) headersState { // Build the headersState storage - store := ctx.KVStore(k.storeKey) + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) return headersState{ cdc: k.cdc, - headers: prefix.NewStore(store, types.HeadersObjectPrefix), - hashToHeight: prefix.NewStore(store, types.HashToHeightPrefix), - hashToWork: prefix.NewStore(store, types.HashToWorkPrefix), - tip: prefix.NewStore(store, types.TipPrefix), + headers: prefix.NewStore(storeAdapter, types.HeadersObjectPrefix), + hashToHeight: prefix.NewStore(storeAdapter, types.HashToHeightPrefix), } } -// CreateHeader Insert the header into the following storages: +// insertHeader Insert the header into the following storages: // - hash->height -// - hash->work -// - (height, hash)->header storage -func (s headersState) CreateHeader(headerInfo *types.BTCHeaderInfo) { - headerHash := headerInfo.Hash - height := headerInfo.Height - cumulativeWork := headerInfo.Work - +// - height -> HeaderInfo +func (s headersState) insertHeader(h *types.BTCHeaderInfo) { // Get necessary keys according - headersKey := types.HeadersObjectKey(height, headerHash) - heightKey := types.HeadersObjectHeightKey(headerHash) - workKey := types.HeadersObjectWorkKey(headerHash) + headersKey := types.HeadersObjectKey(h.Height) + heightKey := types.HeadersObjectHeightKey(h.Hash) // save concrete object - s.headers.Set(headersKey, s.cdc.MustMarshal(headerInfo)) - // map header to height - s.hashToHeight.Set(heightKey, sdk.Uint64ToBigEndian(height)) - // map header to work - workBytes, err := cumulativeWork.Marshal() - if err != nil { - panic("Work cannot be marshalled") - } - s.hashToWork.Set(workKey, workBytes) - - s.updateLongestChain(headerInfo) -} - -// CreateTip sets the provided header as the tip -func (s headersState) CreateTip(headerInfo *types.BTCHeaderInfo) { - // Retrieve the key for the tip storage - tipKey := types.TipKey() - s.tip.Set(tipKey, s.cdc.MustMarshal(headerInfo)) + s.headers.Set(headersKey, s.cdc.MustMarshal(h)) + s.hashToHeight.Set(heightKey, sdk.Uint64ToBigEndian(h.Height)) } -// GetHeader Retrieve a header by its height and hash -func (s headersState) GetHeader(height uint64, hash *bbn.BTCHeaderHashBytes) (*types.BTCHeaderInfo, error) { - // Keyed by (height, hash) - headersKey := types.HeadersObjectKey(height, hash) - - if !s.headers.Has(headersKey) { - return nil, types.ErrHeaderDoesNotExist.Wrap("no header with provided height and hash") - } - // Retrieve the raw bytes - rawBytes := s.headers.Get(headersKey) +func (s headersState) deleteHeader(h *types.BTCHeaderInfo) { + // Get necessary keys + headersKey := types.HeadersObjectKey(h.Height) + heightKey := types.HeadersObjectHeightKey(h.Hash) - return headerInfoFromStoredBytes(s.cdc, rawBytes), nil + // save concrete object + s.headers.Delete(headersKey) + s.hashToHeight.Delete(heightKey) } -// GetHeaderHeight Retrieve the Height of a header -func (s headersState) GetHeaderHeight(hash *bbn.BTCHeaderHashBytes) (uint64, error) { - // Keyed by hash - hashKey := types.HeadersObjectHeightKey(hash) - - // Retrieve the raw bytes for the height - if !s.hashToHeight.Has(hashKey) { - return 0, types.ErrHeaderDoesNotExist.Wrap("no header with provided hash") - } - - bz := s.hashToHeight.Get(hashKey) - // Convert to uint64 form - height := sdk.BigEndianToUint64(bz) - return height, nil -} +func (s headersState) rollBackHeadersUpTo(height uint64) { + headersToDelete := make([]*types.BTCHeaderInfo, 0) -// GetHeaderWork Retrieve the work of a header -func (s headersState) GetHeaderWork(hash *bbn.BTCHeaderHashBytes) (*sdkmath.Uint, error) { - // Keyed by hash - hashKey := types.HeadersObjectHeightKey(hash) - // Retrieve the raw bytes for the work - bz := s.hashToWork.Get(hashKey) - if bz == nil { - return nil, types.ErrHeaderDoesNotExist.Wrap("no header with provided hash") - } + handleInfoFn := func(header *types.BTCHeaderInfo) bool { + if len(headersToDelete) == 0 && height >= header.Height { + // first header in iteration i.e the one with highest height and rollback to block + // higher than current tip has been requested. stop the iteration + return true + } - // Convert to *big.Int form - work := new(sdkmath.Uint) - err := work.Unmarshal(bz) - if err != nil { - panic("Stored header cannot be unmarshalled to sdk.Uint") - } - return work, nil -} + if header.Height == height { + return true + } -// GetHeaderByHash Retrieve a header by its hash -func (s headersState) GetHeaderByHash(hash *bbn.BTCHeaderHashBytes) (*types.BTCHeaderInfo, error) { - // Get the height of the header in order to use it along with the hash - // as a (height, hash) key for the object storage - height, err := s.GetHeaderHeight(hash) - if err != nil { - return nil, err + headersToDelete = append(headersToDelete, header) + return false } - return s.GetHeader(height, hash) -} -// GetBaseBTCHeader retrieves the BTC header with the minimum height -func (s headersState) GetBaseBTCHeader() *types.BTCHeaderInfo { - // Retrieve the canonical chain - canonicalChain := s.GetMainChain() - // If the canonical chain is empty, then there is no base header - if len(canonicalChain) == 0 { - return nil - } - // The base btc header is the oldest one from the canonical chain - return canonicalChain[len(canonicalChain)-1] -} + s.IterateReverseHeaders(handleInfoFn) -// GetTip returns the tip of the canonical chain -func (s headersState) GetTip() *types.BTCHeaderInfo { - if !s.TipExists() { - return nil + // delete rollbacked headers from storage and set up new tip + for _, header := range headersToDelete { + s.deleteHeader(header) } - // Get the key to the tip storage - tipKey := types.TipKey() - return headerInfoFromStoredBytes(s.cdc, s.tip.Get(tipKey)) } -// HeadersByHeight Retrieve headers by their height using an accumulator function -func (s headersState) HeadersByHeight(height uint64, f func(*types.BTCHeaderInfo) bool) { - // The s.headers store is keyed by (height, hash) - // By getting the prefix key using the height, - // we are getting a store of `hash -> header` that contains all hashes - // with a particular height. - store := prefix.NewStore(s.headers, sdk.Uint64ToBigEndian(height)) +// GetHeaderByHeight Retrieve a header by its height and hash +func (s headersState) GetHeaderByHeight(height uint64) (*types.BTCHeaderInfo, error) { + headersKey := types.HeadersObjectKey(height) - iter := store.Iterator(nil, nil) - defer iter.Close() + // Retrieve the raw bytes + rawBytes := s.headers.Get(headersKey) - // Iterate through the prefix store and retrieve each header object. - // Using the header object invoke the accumulator function. - for ; iter.Valid(); iter.Next() { - header := headerInfoFromStoredBytes(s.cdc, iter.Value()) - // The accumulator function notifies us whether the iteration should stop. - stop := f(header) - if stop { - break - } + if rawBytes == nil { + return nil, types.ErrHeaderDoesNotExist.Wrap("no header with provided height") } -} -// getDescendingHeadersUpTo returns a collection of descending headers according to their height -func (s headersState) getDescendingHeadersUpTo(startHeight uint64, depth uint64) []*types.BTCHeaderInfo { - var headers []*types.BTCHeaderInfo - s.iterateReverseHeaders(func(header *types.BTCHeaderInfo) bool { - // Use `depth+1` because we want to first gather all the headers - // with a depth of `depth`. - if startHeight-header.Height == depth+1 { - return true - } - headers = append(headers, header) - return false - }) - return headers + return headerInfoFromStoredBytes(s.cdc, rawBytes), nil } -// GetHeaderAncestryUpTo returns a list of headers starting from the header parameter and leading to -// -// the header that has a `depth` distance from it. -func (s headersState) GetHeaderAncestryUpTo(currentHeader *types.BTCHeaderInfo, depth uint64) []*types.BTCHeaderInfo { - // Retrieve a collection of headers in descending height order - // Use depth+1 since we want all headers at the depth height. - headers := s.getDescendingHeadersUpTo(currentHeader.Height, depth) - - var chain []*types.BTCHeaderInfo - chain = append(chain, currentHeader) - // Set the current header to be that of the tip - // Iterate through the collection and: - // - Discard anything with a higher height from the current header - // - Find the parent of the header and set the current header to it - // Return the current header - for _, header := range headers { - if currentHeader.HasParent(header) { - currentHeader = header - chain = append(chain, header) - } - } +// GetHeaderByHash Retrieve a header by its hash +func (s headersState) GetHeaderByHash(hash *bbn.BTCHeaderHashBytes) (*types.BTCHeaderInfo, error) { + // Keyed by hash + hashKey := types.HeadersObjectHeightKey(hash) - return chain -} + heightBytes := s.hashToHeight.Get(hashKey) -// 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 (s headersState) GetMainChainUpTo(depth uint64) []*types.BTCHeaderInfo { - // If there is no tip, there is no base header - if !s.TipExists() { - return nil + if heightBytes == nil { + return nil, types.ErrHeaderDoesNotExist.Wrap("no header with provided hash") } - return s.GetHeaderAncestryUpTo(s.GetTip(), depth) -} -// GetMainChain retrieves the main chain as a collection of block headers starting from the tip -// -// and ending on the base BTC header. -func (s headersState) GetMainChain() []*types.BTCHeaderInfo { - if !s.TipExists() { - return nil - } - tip := s.GetTip() - // By providing the depth as the tip.Height, we ensure that we will go as deep as possible - return s.GetMainChainUpTo(tip.Height) -} + // Retrieve the raw bytes + headerBytes := s.headers.Get(heightBytes) -// GetHighestCommonAncestor traverses the ancestors of both headers -// to identify the common ancestor with the highest height -func (s headersState) GetHighestCommonAncestor(header1 *types.BTCHeaderInfo, header2 *types.BTCHeaderInfo) *types.BTCHeaderInfo { - // The algorithm works as follows: - // 1. Initialize a hashmap hash -> bool denoting whether the hash - // of an ancestor of either header1 or header2 has been encountered - // 2. Maintain ancestor1 and ancestor2 as variables that point - // to the current ancestor hash of the header1 and header2 parameters - // 3. Whenever a node is encountered with a hash that is equal to ancestor{1,2}, - // update the ancestor{1,2} variables. - // 4. If ancestor1 or ancestor2 is set to the hash table, - // then that's the hash of the earliest ancestor - // 5. Using the hash of the heighest ancestor wait until we get the header bytes - // in order to avoid an extra access. - if header1.HasParent(header2) { - return header2 - } - if header2.HasParent(header1) { - return header1 + if headerBytes == nil { + height := sdk.BigEndianToUint64(heightBytes) + // panic here, as it means we got mapping hash->height but no mapping height->header + // and those should always be in sync + errMsg := fmt.Sprintf("header height exists but header does not. HeaderHash: %s, HeaderHeight: %d", hash.String(), height) + panic(errMsg) } - ancestor1 := header1.Hash - ancestor2 := header2.Hash - encountered := make(map[string]bool, 0) - encountered[ancestor1.String()] = true - encountered[ancestor2.String()] = true - var found *bbn.BTCHeaderHashBytes = nil - - var resHeader *types.BTCHeaderInfo = nil - - s.iterateReverseHeaders(func(header *types.BTCHeaderInfo) bool { - // During iteration, we will encounter an ancestor for which its header hash - // has been set on the hash map. - // However, we do not have the entry yet, so we set the found flag to that hash - // and when we encounter it during iteration we return it. - if found != nil && header.Hash.Eq(found) { - resHeader = header - return true - } else { - if ancestor1.Eq(header.Hash) { - ancestor1 = header.Header.ParentHash() - if encountered[ancestor1.String()] { - found = ancestor1 - } - encountered[ancestor1.String()] = true - } - if ancestor2.Eq(header.Hash) { - ancestor2 = header.Header.ParentHash() - if encountered[ancestor2.String()] { - found = ancestor2 - } - encountered[ancestor2.String()] = true - } - } - return false - }) - return resHeader + return headerInfoFromStoredBytes(s.cdc, headerBytes), nil } -// GetInOrderAncestorsUntil returns the list of nodes starting from the block *after* the `ancestor` and ending with the `descendant`. -func (s headersState) GetInOrderAncestorsUntil(descendant *types.BTCHeaderInfo, ancestor *types.BTCHeaderInfo) []*types.BTCHeaderInfo { - if ancestor.Height > descendant.Height { - panic("Ancestor has a higher height than descendant") - } - if ancestor.Height == descendant.Height { - // return an empty list - return []*types.BTCHeaderInfo{} - } - - if descendant.HasParent(ancestor) { - return []*types.BTCHeaderInfo{descendant} - } - - ancestors := s.GetHeaderAncestryUpTo(descendant, descendant.Height-ancestor.Height) - if !ancestors[len(ancestors)-1].Eq(ancestor) { - // `ancestor` is not an ancestor of `descendant`, return an empty list - return []*types.BTCHeaderInfo{} - } - - // Discard the last element of the ancestry which corresponds to `ancestor` - ancestors = ancestors[:len(ancestors)-1] - - // Reverse the ancestry - for i, j := 0, len(ancestors)-1; i < j; i, j = i+1, j-1 { - ancestors[i], ancestors[j] = ancestors[j], ancestors[i] +// GetTip returns the tip of the canonical chain +func (s headersState) GetTip() *types.BTCHeaderInfo { + var tip *types.BTCHeaderInfo + handleTipFn := func(header *types.BTCHeaderInfo) bool { + // first retrieved header is tip + tip = header + return true } - - return ancestors + s.IterateReverseHeaders(handleTipFn) + return tip } // HeaderExists Check whether a hash is maintained in storage @@ -326,36 +135,44 @@ func (s headersState) HeaderExists(hash *bbn.BTCHeaderHashBytes) bool { if hash == nil { return false } - return s.hashToHeight.Has(hash.MustMarshal()) + + _, err := s.GetHeaderByHash(hash) + + return err == nil } // TipExists checks whether the tip of the canonical chain has been set func (s headersState) TipExists() bool { - tipKey := types.TipKey() - return s.tip.Has(tipKey) + return s.GetTip() != nil } -// updateLongestChain checks whether the tip should be updated and returns true if it does -func (s headersState) updateLongestChain(headerInfo *types.BTCHeaderInfo) { - // If there is no existing tip, then the header is set as the tip - if !s.TipExists() { - s.CreateTip(headerInfo) - return - } - - // Get the current tip header hash - tip := s.GetTip() +func (s headersState) IterateReverseHeaders(fn func(*types.BTCHeaderInfo) bool) { + // Iterate it in reverse in order to get the highest heights first + iter := s.headers.ReverseIterator(nil, nil) + defer iter.Close() - // If the work of the current tip is less than the work of the provided header, - // the provided header is set as the tip. - if headerInfo.Work.GT(*tip.Work) { - s.CreateTip(headerInfo) + for ; iter.Valid(); iter.Next() { + header := headerInfoFromStoredBytes(s.cdc, iter.Value()) + stop := fn(header) + if stop { + break + } } } -func (s headersState) iterateReverseHeaders(fn func(*types.BTCHeaderInfo) bool) { - // Iterate it in reverse in order to get highest heights first - iter := s.headers.ReverseIterator(nil, nil) +// IterateForwardHeaders iterates over all headers in store in increasing order +// - if startPoint is 0, it will start from the lowest height +// - if startPoint is lower that the lowest height, it will start from the lowest height +// - if startPoint is higher than the highest height, it will not iterate at all i.e provided +// callback will not be called +func (s headersState) IterateForwardHeaders(startPoint uint64, fn func(*types.BTCHeaderInfo) bool) { + // Iterate it in increasing order to get lowest heights first + var startKey []byte = nil + if startPoint != 0 { + startKey = types.HeadersObjectKey(startPoint) + } + + iter := s.headers.Iterator(startKey, nil) defer iter.Close() for ; iter.Valid(); iter.Next() { @@ -366,3 +183,14 @@ func (s headersState) iterateReverseHeaders(fn func(*types.BTCHeaderInfo) bool) } } } + +func (s headersState) BaseHeader() *types.BTCHeaderInfo { + var baseHeader *types.BTCHeaderInfo + handleBaseHeaderFn := func(header *types.BTCHeaderInfo) bool { + // first retrieved header is base header + baseHeader = header + return true + } + s.IterateForwardHeaders(0, handleBaseHeaderFn) + return baseHeader +} diff --git a/x/btclightclient/keeper/state_test.go b/x/btclightclient/keeper/state_test.go index f7b2aecc4..296509e62 100644 --- a/x/btclightclient/keeper/state_test.go +++ b/x/btclightclient/keeper/state_test.go @@ -4,558 +4,100 @@ import ( "math/rand" "testing" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/testutil/datagen" testkeeper "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) func FuzzHeadersStateCreateHeader(f *testing.F) { /* Checks: 1. A headerInfo provided as an argument leads to the following storage objects being created: - - A (height, headerHash) -> headerInfo object + - A (height) -> headerInfo object - A (headerHash) -> height object - - A (headerHash) -> work object - - A () -> tip object depending on conditions: - * If the tip does not exist, then the headerInfo is the tip - * If the tip exists, and the header inserted has greater work than it, then it becomes the tip Data generation: - Create four headers: 1. The Base header. This will test whether the tip is set. - 2. A header that builds on top of the base header. - This will test whether the tip is properly updated once more work is added. - 3. A header that builds on top of the base header but with less work than (2). - This will test whether the tip is not updated when less work than it is added. - 4. A header that builds on top of the base header but with equal work to (2). - This will test whether the tip is not updated when equal work to it is added. - - No need to create a tree, since this function does not consider the existence or the chain, - it just inserts into state and updates the tip based on a simple work comparison. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - // Generate a tree with a single root node - tree := genRandomTree(r, blcKeeper, ctx, 1, 1) - baseHeader := tree.GetRoot() - - // Test whether the tip and storages are set - tip := blcKeeper.HeadersState(ctx).GetTip() - if tip == nil { - t.Fatalf("Creation of base header did not lead to creation of tip") - } - if !baseHeader.Eq(tip) { - t.Errorf("Tip does not correspond to the one submitted %s %s", baseHeader.Hash, tip.Hash) - } - headerObj, err := blcKeeper.HeadersState(ctx).GetHeader(baseHeader.Height, baseHeader.Hash) - if err != nil { - t.Errorf("Could not retrieve created header") - } - if !baseHeader.Eq(headerObj) { - t.Errorf("Created object does not correspond to the one submitted") - } - work, err := blcKeeper.HeadersState(ctx).GetHeaderWork(baseHeader.Hash) - if err != nil { - t.Errorf("Could not retrieve work of created header") - } - if !baseHeader.Work.Equal(*work) { - t.Errorf("Created object work does not correspond to the one submitted") - } - height, err := blcKeeper.HeadersState(ctx).GetHeaderHeight(baseHeader.Hash) - if err != nil { - t.Errorf("Could not retrieve height of created header") - } - if height != baseHeader.Height { - t.Errorf("Created object height does not correspond to the one submitted") - } - - // Test whether a new header updates the tip. - // The smaller number, the bigger the difficulty - mostDifficulty := sdk.NewUint(10) - lessDifficulty := mostDifficulty.Add(sdk.NewUint(1)) - // Create an object that builds on top of base header - childMostWork := datagen.GenRandomBTCHeaderInfoWithParentAndBits(r, baseHeader, &mostDifficulty) - blcKeeper.HeadersState(ctx).CreateHeader(childMostWork) - // Check whether the tip was updated - tip = blcKeeper.HeadersState(ctx).GetTip() - if tip == nil { - t.Fatalf("Tip became nil instead of getting updated") - } - if !childMostWork.Eq(tip) { - t.Errorf("Tip did not get properly updated") - } - - childEqualWork := datagen.GenRandomBTCHeaderInfoWithParentAndBits(r, baseHeader, &mostDifficulty) - blcKeeper.HeadersState(ctx).CreateHeader(childEqualWork) - // Check whether the tip was updated - tip = blcKeeper.HeadersState(ctx).GetTip() - if !childMostWork.Eq(tip) { - t.Errorf("Tip got updated when it shouldn't") - } - - childLessWork := datagen.GenRandomBTCHeaderInfoWithParentAndBits(r, baseHeader, &lessDifficulty) - blcKeeper.HeadersState(ctx).CreateHeader(childLessWork) - // Check whether the tip was updated - tip = blcKeeper.HeadersState(ctx).GetTip() - if !childMostWork.Eq(tip) { - t.Errorf("Tip got updated when it shouldn't") - } - - }) -} - -func FuzzHeadersStateTipOps(f *testing.F) { - /* - Functions Tested: - 1. CreateTip - 2. GetTip - 3. TipExists - - Checks: - * CreateTip - 1. The `headerInfo` object passed is set as the tip. - * GetTip - 1. If the tip does not exist, nil is returned. - 2. The element maintained in the tip storage is returned. - * TipExists - 1. Returns true/false depending on the existence of a tip. - - Data generation: - - Create two headers: - 1. A header that will be set as the tip. - 2. A header that will override it. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - headerInfo1 := datagen.GenRandomBTCHeaderInfo(r) - headerInfo2 := datagen.GenRandomBTCHeaderInfo(r) - - retrievedHeaderInfo := blcKeeper.HeadersState(ctx).GetTip() - if retrievedHeaderInfo != nil { - t.Errorf("GetTip did not return nil for empty tip") - } - - if blcKeeper.HeadersState(ctx).TipExists() { - t.Errorf("TipExists returned true when no tip has been set") - } - - blcKeeper.HeadersState(ctx).CreateTip(headerInfo1) - retrievedHeaderInfo = blcKeeper.HeadersState(ctx).GetTip() - - if !headerInfo1.Eq(retrievedHeaderInfo) { - t.Errorf("HeaderInfo object did not get stored in tip") - } - - if !blcKeeper.HeadersState(ctx).TipExists() { - t.Errorf("TipExists returned false when a tip had been set") - } - - blcKeeper.HeadersState(ctx).CreateTip(headerInfo2) - retrievedHeaderInfo = blcKeeper.HeadersState(ctx).GetTip() - if !headerInfo2.Eq(retrievedHeaderInfo) { - t.Errorf("Tip did not get overriden") - } - if !blcKeeper.HeadersState(ctx).TipExists() { - t.Errorf("TipExists returned false when a tip had been set") - } - }) -} - -func FuzzHeadersStateGetHeaderOps(f *testing.F) { - /* - Functions tested: - 1. GetHeader - 2. GetHeaderHeight - 3. GetHeaderWork - 4. GetHeaderByHash - 5. HeaderExists - - Checks: - * GetHeader - 1. If the header specified by a height and a hash does not exist, (nil, error) is returned - 2. If the header specified by a height and a hash exists, (headerInfo, nil) is returned - * GetHeaderHeight - 1. If the header specified by the hash does not exist, (0, error) is returned - 2. If the header specified by the hash exists, (height, nil) is returned - * GetHeaderWork - 1. If the header specified by the hash does not exist, (nil, error) is returned - 2. If the header specified by the hash exists, (work, nil) is returned. - * GetHeaderByHash - 1. If the header specified by the hash does not exist (nil, error) is returned - 2. If the header specified by the hash exists (headerInfo, nil) is returned. - * HeaderExists - 1. Returns false if the header passed is nil. - 2. Returns true/false depending on the existence of the header. - - Data generation: - - Create a header and store it using the `CreateHeader` method. Do retrievals to check conditions. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - headerInfo := datagen.GenRandomBTCHeaderInfo(r) - wrongHash := datagen.MutateHash(r, headerInfo.Hash) - wrongHeight := headerInfo.Height + datagen.RandomInt(r, 10) + 1 - - // ****** HeaderExists tests ****** - if blcKeeper.HeadersState(ctx).HeaderExists(nil) { - t.Errorf("HeaderExists returned true for nil input") - } - if blcKeeper.HeadersState(ctx).HeaderExists(headerInfo.Hash) { - t.Errorf("HeaderExists returned true for not created header") - } - blcKeeper.HeadersState(ctx).CreateHeader(headerInfo) - if !blcKeeper.HeadersState(ctx).HeaderExists(headerInfo.Hash) { - t.Errorf("HeaderExists returned false for created header") - } - // ****** GetHeader tests ****** - // correct retrieval - retrievedHeaderInfo, err := blcKeeper.HeadersState(ctx).GetHeader(headerInfo.Height, headerInfo.Hash) - if err != nil { - t.Errorf("GetHeader returned error for valid retrieval: %s", err) - } - if retrievedHeaderInfo == nil || !retrievedHeaderInfo.Eq(headerInfo) { - t.Errorf("GetHeader returned a header that is nil or does not equal the one inserted") - } - retrievedHeaderInfo, err = blcKeeper.HeadersState(ctx).GetHeader(headerInfo.Height, wrongHash) - if retrievedHeaderInfo != nil || err == nil { - t.Errorf("GetHeader returned a filled HeaderInfo or the error is nil for invalid input") - } - - retrievedHeaderInfo, err = blcKeeper.HeadersState(ctx).GetHeader(wrongHeight, headerInfo.Hash) - if retrievedHeaderInfo != nil || err == nil { - t.Errorf("GetHeader returned a filled HeaderInfo or the error is nil for invalid input") - } - - retrievedHeaderInfo, err = blcKeeper.HeadersState(ctx).GetHeader(wrongHeight, wrongHash) - if retrievedHeaderInfo != nil || err == nil { - t.Errorf("GetHeader returned a filled HeaderInfo or the error is nil for invalid input") - } - - // ****** GetHeaderHeight tests ****** - height, err := blcKeeper.HeadersState(ctx).GetHeaderHeight(headerInfo.Hash) - if err != nil { - t.Errorf("GetHeaderHeight returned an error for valid retrieval: %s", err) - } - if height != headerInfo.Height { - t.Errorf("GetHeaderHeight returned incorrect height") - } - height, err = blcKeeper.HeadersState(ctx).GetHeaderHeight(wrongHash) - if err == nil || height != 0 { - t.Errorf("GetHeaderHeight returned nil error or a height different than zero for invalid input") - } - - // ****** GetHeaderWork tests ****** - work, err := blcKeeper.HeadersState(ctx).GetHeaderWork(headerInfo.Hash) - if err != nil { - t.Errorf("GetHeaderWork returned an error for valid retrieval: %s", err) - } - if work == nil || !work.Equal(*headerInfo.Work) { - t.Errorf("GetHeaderWork returned nil or incorrect work") - } - work, err = blcKeeper.HeadersState(ctx).GetHeaderWork(wrongHash) - if err == nil || work != nil { - t.Errorf("GetHeaderWork returned nil error or a work different than nil for invalid input") - } - - // ****** GetHeaderByHash tests ****** - retrievedHeaderInfo, err = blcKeeper.HeadersState(ctx).GetHeaderByHash(headerInfo.Hash) - if err != nil { - t.Errorf("GetHeaderByHash returned an error for valid retrieval: %s", err) - } - if retrievedHeaderInfo == nil || !retrievedHeaderInfo.Eq(headerInfo) { - t.Errorf("GetHeaderByHash returned a header that is nil or does not equal the one inserted") - } - - retrievedHeaderInfo, err = blcKeeper.HeadersState(ctx).GetHeaderByHash(wrongHash) - if retrievedHeaderInfo != nil || err == nil { - t.Errorf("GetHeaderByHash returned a filled HeaderInfo or the error is nil for invalid input") - } - }) -} - -func FuzzHeadersStateGetBaseBTCHeader(f *testing.F) { - /* - Checks: - 1. If no headers exist, nil is returned - 2. The oldest element of the main chain is returned. - - Data generation: - - Generate a random tree and retrieve the main chain from it. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - nilBaseHeader := blcKeeper.HeadersState(ctx).GetBaseBTCHeader() - if nilBaseHeader != nil { - t.Errorf("Non-existent base BTC header led to non-nil return") - } - - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - expectedBaseHeader := tree.GetRoot() - - gotBaseHeader := blcKeeper.HeadersState(ctx).GetBaseBTCHeader() - - if !expectedBaseHeader.Eq(gotBaseHeader) { - t.Errorf("Expected base header %s got %s", expectedBaseHeader.Hash, gotBaseHeader.Hash) - } - }) -} - -func FuzzHeadersStateHeadersByHeight(f *testing.F) { - /* - Checks: - 1. If the height does not correspond to any headers, the function parameter is never invoked. - 2. If the height corresponds to headers, the function is invoked for all of those headers. - 3. If the height corresponds to headers, the function is invoked until a stop signal is given. - - Data generation: - - Generate a `rand.Intn(N)` number of headers with a particular height and insert them into storage. - - The randomness of the number of headers should guarantee that (1) and (2) are observed. - - Generate a random stop signal 1/N times. + 2. Create random chain of of headers, and insert them into the state + 3. All operations should be consistent with each other. */ datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - maxHeaders := 256 // maximum 255 headers with particular height - numHeaders := datagen.RandomInt(r, maxHeaders) - - // This will contain a mapping between all the header hashes that were created - // and a boolean value. - hashCount := make(map[string]bool) - // Setup a tree with a single header - tree := genRandomTree(r, blcKeeper, ctx, 1, 1) - baseHeader := tree.GetRoot() - height := baseHeader.Height + 1 + state := blcKeeper.HeadersState(ctx) - // Generate numHeaders with particular height - var i uint64 - for i = 0; i < numHeaders; i++ { - headerInfo := datagen.GenRandomBTCHeaderInfoWithParent(r, baseHeader) - hashCount[headerInfo.Hash.MarshalHex()] = true - blcKeeper.InsertHeader(ctx, headerInfo.Header) //nolint:errcheck - } + // operations no empty state + require.Nil(t, state.GetTip()) + require.Nil(t, state.BaseHeader()) + require.False(t, state.TipExists()) - var headersAdded uint64 = 0 - var stopHeight uint64 = 0 - blcKeeper.HeadersState(ctx).HeadersByHeight(height, func(header *types.BTCHeaderInfo) bool { - headersAdded += 1 - if _, ok := hashCount[header.Hash.MarshalHex()]; !ok { - t.Errorf("HeadersByHeight returned header that was not created") - } - hashCount[header.Hash.MarshalHex()] = true - if datagen.OneInN(r, maxHeaders) { - // Only set it once - if stopHeight != 0 { - stopHeight = headersAdded - } - return true - } + numForward := 0 + numBackward := 0 + state.IterateForwardHeaders(0, func(headerInfo *types.BTCHeaderInfo) bool { + numForward++ return false }) - if stopHeight != 0 && stopHeight != headersAdded { - t.Errorf("Stop signal was not respected. %d headers were added while %d were expected", stopHeight, headersAdded) - } - - for _, cnt := range hashCount { - if !cnt && headersAdded == numHeaders { - // If there is a header hash that the count is not set - // and all the headers were iterated, then something went wrong - t.Errorf("Function did not iterate all headers") - } - } - }) -} - -func FuzzHeadersStateGetMainChain(f *testing.F) { - /* - Functions Tested: - 1. GetMainChain - 2. GetMainChainUpTo - - Checks: - * GetMainChain - 1. We get the entire main chain. - * GetMainChainUpTo - 1. We get the main chain containing `depth + 1` elements. - - Data generation: - - Generate a random tree and retrieve the main chain from it. - - Randomly generate the depth - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - expectedMainChain := tree.GetMainChain() - gotMainChain := blcKeeper.HeadersState(ctx).GetMainChain() - - if len(expectedMainChain) != len(gotMainChain) { - t.Fatalf("Expected main chain length of %d, got %d", len(expectedMainChain), len(gotMainChain)) - } - - for i := 0; i < len(expectedMainChain); i++ { - if !expectedMainChain[i].Eq(gotMainChain[i]) { - t.Errorf("Expected header %s at position %d, got %s", expectedMainChain[i].Hash, i, gotMainChain[i].Hash) - } - } - - // depth is a random integer - upToDepth := datagen.RandomInt(r, len(expectedMainChain)) - expectedMainChainUpTo := expectedMainChain[:upToDepth+1] - gotMainChainUpTo := blcKeeper.HeadersState(ctx).GetMainChainUpTo(upToDepth) - if len(expectedMainChainUpTo) != len(gotMainChainUpTo) { - t.Fatalf("Expected main chain length of %d, got %d", len(expectedMainChainUpTo), len(gotMainChainUpTo)) - } - - for i := 0; i < len(expectedMainChainUpTo); i++ { - if !expectedMainChainUpTo[i].Eq(gotMainChainUpTo[i]) { - t.Errorf("Expected header %s at position %d, got %s", expectedMainChainUpTo[i].Hash, i, gotMainChainUpTo[i].Hash) - } - } - }) -} - -func FuzzHeadersStateGetHighestCommonAncestor(f *testing.F) { - /* - Checks: - 1. The header returned is an ancestor of both headers. - 2. There is no header that is an ancestor of both headers that has a higher height - than the one returned. - 3. There is always a header that is returned, since all headers are built on top of the same root. - - Data generation: - - Generate a random tree of headers and store it. - - Select two random headers and call `GetHighestCommonAncestor` for them. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - // Generate a random tree with at least one node - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - // Retrieve a random common ancestor - commonAncestor := tree.RandomNode(r) - - // Generate two child nodes for the common ancestor - childRoot1 := datagen.GenRandomBTCHeaderInfoWithParent(r, commonAncestor) - childRoot2 := datagen.GenRandomBTCHeaderInfoWithParent(r, commonAncestor) - if tree.Contains(childRoot1) || tree.Contains(childRoot2) { - // Unlucky case where we get the same hash. Should be extremely rare. - // Instead of adding extra complexity to this test case, just skip it - t.Skip() - } - // Insert them into storage - blcKeeper.InsertHeader(ctx, childRoot1.Header) //nolint:errcheck - blcKeeper.InsertHeader(ctx, childRoot2.Header) //nolint:errcheck - // Add them into the data structures maintained by the tree - tree.Add(childRoot1, commonAncestor) - tree.Add(childRoot2, commonAncestor) - - // Generate subtrees rooted at the descendant nodes - genRandomTreeWithParent(r, blcKeeper, ctx, 1, 10, childRoot1, tree) - genRandomTreeWithParent(r, blcKeeper, ctx, 1, 10, childRoot2, tree) - - // Get a random descendant from each of the subtrees - descendant1 := tree.RandomDescendant(r, childRoot1) - descendant2 := tree.RandomDescendant(r, childRoot2) - - retrievedHighestCommonAncestor := blcKeeper.HeadersState(ctx).GetHighestCommonAncestor(descendant1, descendant2) - if retrievedHighestCommonAncestor == nil { - t.Fatalf("No common ancestor found between the nodes %s and %s. Expected ancestor: %s", descendant1.Hash, descendant2.Hash, commonAncestor.Hash) - } - if !commonAncestor.Eq(retrievedHighestCommonAncestor) { - t.Errorf("Did not retrieve the correct highest common ancestor. Got %s, expected %s", retrievedHighestCommonAncestor.Hash, commonAncestor.Hash) - } - }) -} - -func FuzzHeadersStateGetInOrderAncestorsUntil(f *testing.F) { - /* - Checks: - 1. All the ancestors are contained in the returned list. - 2. The ancestors do not include the `ancestor` parameter. - 3. The ancestors are in order starting from the `ancestor`'s child and leading to the `descendant` parameter. - - Data generation: - - Generate a random tree of headers and store it. - - Select a random header which will serve as the `descendant`. Cannot be the base header. - - Select a random header that is an ancestor of `descendant`. - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - - // Generate a tree of any size. - // We can work with even one header, since this should lead to an empty result. - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - - // Get a random header from the tree - descendant := tree.RandomNode(r) - // Get a random ancestor from it - ancestor := tree.RandomAncestor(r, descendant) - // Get the ancestry of the descendant. - // It is in reverse order from the one that GetInOrderAncestorsUntil returns, since it starts with the descendant. - expectedAncestorsReverse := tree.GetNodeAncestryUpTo(descendant, ancestor) - gotAncestors := blcKeeper.HeadersState(ctx).GetInOrderAncestorsUntil(descendant, ancestor) - if len(gotAncestors) != len(expectedAncestorsReverse) { - t.Errorf("Got different ancestor list sizes. Expected %d got %d", len(expectedAncestorsReverse), len(gotAncestors)) - } - - for i := 0; i < len(expectedAncestorsReverse); i++ { - reverseIdx := len(expectedAncestorsReverse) - i - 1 - if !expectedAncestorsReverse[i].Eq(gotAncestors[reverseIdx]) { - t.Errorf("Ancestors do not match. Expected %s got %s", expectedAncestorsReverse[i].Hash, gotAncestors[reverseIdx].Hash) - } - } - }) -} - -func FuzzHeadersStateGetHeaderAncestryUpTo(f *testing.F) { - /* - Checks: - 1. All the ancestors up to the depth are in the returned list. - 2. The ancestors start from the parameter and lead to the ancestor. - - Data generation: - - Generate a random tree of headers and store it. - - Select a random header which will serve as the `header` parameter. - - Select a random depth in the range of [0, header.Height-baseHeader.Height] - */ - datagen.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - blcKeeper, ctx := testkeeper.BTCLightClientKeeper(t) - tree := genRandomTree(r, blcKeeper, ctx, 1, 10) - - descendant := tree.RandomNode(r) - ancestor := tree.RandomAncestor(r, descendant) - - ancestors := blcKeeper.HeadersState(ctx).GetHeaderAncestryUpTo(descendant, descendant.Height-ancestor.Height) - // Use the parent of the ancestor since UpTo does not include the ancestor in the result - expectedAncestors := tree.GetNodeAncestryUpTo(descendant, tree.GetParent(ancestor)) - - if len(ancestors) != len(expectedAncestors) { - t.Errorf("Got different ancestor list sizes. Expected %d, got %d", len(expectedAncestors), len(ancestors)) - } - - for i := 0; i < len(ancestors); i++ { - if !ancestors[i].Eq(expectedAncestors[i]) { - t.Errorf("Ancestors do not match. Expected %s, got %s", expectedAncestors[i].Hash, ancestors[i].Hash) - } + state.IterateReverseHeaders(func(headerInfo *types.BTCHeaderInfo) bool { + numBackward++ + return false + }) + require.Equal(t, 0, numForward) + require.Equal(t, 0, numBackward) + + _, err := state.GetHeaderByHeight(datagen.GenRandomBTCHeight(r)) + require.Error(t, err) + rh := datagen.GenRandomBtcdHash(r) + randomHash := bbn.NewBTCHeaderHashBytesFromChainhash(&rh) + _, err = state.GetHeaderByHash(&randomHash) + require.Error(t, err) + + // 10 to 60 headers + chainLength := datagen.RandomInt(r, 50) + 10 + // height from 10 to 60 + initchainHeight := datagen.RandomInt(r, 50) + 10 + + // populate the state with random chain + _, chain := genRandomChain( + t, + r, + blcKeeper, + ctx, + initchainHeight, + chainLength, + ) + + // operations populates state + require.NotNil(t, state.GetTip()) + require.NotNil(t, state.BaseHeader()) + require.True(t, state.TipExists()) + + numForward = 0 + numBackward = 0 + state.IterateForwardHeaders(0, func(headerInfo *types.BTCHeaderInfo) bool { + numForward++ + return false + }) + state.IterateReverseHeaders(func(headerInfo *types.BTCHeaderInfo) bool { + numBackward++ + return false + }) + require.Equal(t, chainLength+1, uint64(numForward)) + require.Equal(t, chainLength+1, uint64(numBackward)) + + chainInfos := chain.GetChainInfo() + + for _, info := range chainInfos { + byHash, err := state.GetHeaderByHash(info.Hash) + require.NoError(t, err) + byHeight, err := state.GetHeaderByHeight(info.Height) + require.NoError(t, err) + require.True(t, allFieldsEqual(byHash, byHeight)) + require.True(t, allFieldsEqual(byHash, info)) } }) } diff --git a/x/btclightclient/keeper/triggers.go b/x/btclightclient/keeper/triggers.go index b3be7b9cc..8b9bdaea1 100644 --- a/x/btclightclient/keeper/triggers.go +++ b/x/btclightclient/keeper/triggers.go @@ -1,27 +1,28 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/btclightclient/types" sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) triggerHeaderInserted(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) triggerHeaderInserted(ctx context.Context, headerInfo *types.BTCHeaderInfo) { // Trigger AfterBTCHeaderInserted hook k.AfterBTCHeaderInserted(ctx, headerInfo) // Emit HeaderInserted event - ctx.EventManager().EmitTypedEvent(&types.EventBTCHeaderInserted{Header: headerInfo}) //nolint:errcheck + sdk.UnwrapSDKContext(ctx).EventManager().EmitTypedEvent(&types.EventBTCHeaderInserted{Header: headerInfo}) //nolint:errcheck } -func (k Keeper) triggerRollBack(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) triggerRollBack(ctx context.Context, headerInfo *types.BTCHeaderInfo) { // Trigger AfterBTCRollBack hook k.AfterBTCRollBack(ctx, headerInfo) // Emit BTCRollBack event - ctx.EventManager().EmitTypedEvent(&types.EventBTCRollBack{Header: headerInfo}) //nolint:errcheck + sdk.UnwrapSDKContext(ctx).EventManager().EmitTypedEvent(&types.EventBTCRollBack{Header: headerInfo}) //nolint:errcheck } -func (k Keeper) triggerRollForward(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (k Keeper) triggerRollForward(ctx context.Context, headerInfo *types.BTCHeaderInfo) { // Trigger AfterBTCRollForward hook k.AfterBTCRollForward(ctx, headerInfo) // Emit BTCRollForward event - ctx.EventManager().EmitTypedEvent(&types.EventBTCRollForward{Header: headerInfo}) //nolint:errcheck + sdk.UnwrapSDKContext(ctx).EventManager().EmitTypedEvent(&types.EventBTCRollForward{Header: headerInfo}) //nolint:errcheck } diff --git a/x/btclightclient/keeper/utils_test.go b/x/btclightclient/keeper/utils_test.go index e8cab00ea..413918cc3 100644 --- a/x/btclightclient/keeper/utils_test.go +++ b/x/btclightclient/keeper/utils_test.go @@ -1,12 +1,19 @@ package keeper_test import ( - "fmt" + "context" + "math/big" + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/keeper" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "math/rand" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" ) // Mock hooks interface @@ -31,52 +38,87 @@ func NewMockHooks() *MockHooks { } } -func (m *MockHooks) AfterBTCRollForward(_ sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (m *MockHooks) AfterBTCRollForward(_ context.Context, headerInfo *types.BTCHeaderInfo) { m.AfterBTCRollForwardStore = append(m.AfterBTCRollForwardStore, headerInfo) } -func (m *MockHooks) AfterBTCRollBack(_ sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (m *MockHooks) AfterBTCRollBack(_ context.Context, headerInfo *types.BTCHeaderInfo) { m.AfterBTCRollBackStore = append(m.AfterBTCRollBackStore, headerInfo) } -func (m *MockHooks) AfterBTCHeaderInserted(_ sdk.Context, headerInfo *types.BTCHeaderInfo) { +func (m *MockHooks) AfterBTCHeaderInserted(_ context.Context, headerInfo *types.BTCHeaderInfo) { m.AfterBTCHeaderInsertedStore = append(m.AfterBTCHeaderInsertedStore, headerInfo) } -// Methods for generating trees +func allFieldsEqual(a *types.BTCHeaderInfo, b *types.BTCHeaderInfo) bool { + return a.Height == b.Height && a.Hash.Eq(b.Hash) && a.Header.Eq(b.Header) && a.Work.Equal(*b.Work) +} + +// this function must not be used at difficulty adjustment boundaries, as then +// difficulty adjustment calculation will fail +func genRandomChain( + t *testing.T, + r *rand.Rand, + k *keeper.Keeper, + ctx context.Context, + initialHeight uint64, + chainLength uint64, +) (*types.BTCHeaderInfo, *datagen.BTCHeaderPartialChain) { + genesisHeader := datagen.NewBTCHeaderChainWithLength(r, initialHeight, 0, 1) + genesisHeaderInfo := genesisHeader.GetChainInfo()[0] + k.SetBaseBTCHeader(ctx, *genesisHeaderInfo) + randomChain := datagen.NewBTCHeaderChainFromParentInfo( + r, + genesisHeaderInfo, + uint32(chainLength), + ) + err := k.InsertHeaders(ctx, randomChain.ChainToBytes()) + require.NoError(t, err) + tip := k.GetTipInfo(ctx) + randomChainTipInfo := randomChain.GetTipInfo() + require.True(t, allFieldsEqual(tip, randomChainTipInfo)) + return genesisHeaderInfo, randomChain +} -// genRandomTree generates a tree of headers. It accomplishes this by generating a root -// which will serve as the base header and then invokes the `genRandomTreeWithRoot` utility. -// The `minTreeHeight` and `maxTreeHeight` parameters denote the minimum and maximum height -// of the tree that is generated. For example, a `minTreeHeight` of 1, -// means that the tree should have at least one node (the root), while -// a `maxTreeHeight` of 4, denotes that the maximum height of the tree should be 4. -func genRandomTree(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, minHeight uint64, maxHeight uint64) *datagen.BTCHeaderTree { - tree := datagen.NewBTCHeaderTree() - // Generate the root for the tree - root := datagen.GenRandomBTCHeaderInfo(r) - tree.Add(root, nil) - k.SetBaseBTCHeader(ctx, *root) +func checkTip( + t *testing.T, + ctx context.Context, + blcKeeper *keeper.Keeper, + expectedWork sdkmath.Uint, + expectedHeight uint64, + expectedTipHeader *wire.BlockHeader) { - genRandomTreeWithParent(r, k, ctx, minHeight-1, maxHeight-1, root, tree) + currentTip := blcKeeper.GetTipInfo(ctx) + blockByHeight := blcKeeper.GetHeaderByHeight(ctx, currentTip.Height) + blockByHash := blcKeeper.GetHeaderByHash(ctx, currentTip.Hash) - return tree -} + // Consistency check between tip and block by height and block by hash + require.NotNil(t, blockByHeight) + require.NotNil(t, currentTip) + require.NotNil(t, blockByHash) + require.True(t, allFieldsEqual(currentTip, blockByHeight)) + require.True(t, allFieldsEqual(currentTip, blockByHash)) -// genRandomTreeWithParent is a utility function for inserting the headers -// While the tree is generated, the headers that are generated for it are inserted into storage. -func genRandomTreeWithParent(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, minHeight uint64, - maxHeight uint64, root *types.BTCHeaderInfo, tree *datagen.BTCHeaderTree) { + // check all tip fields + require.True(t, currentTip.Work.Equal(expectedWork)) + require.Equal(t, currentTip.Height, expectedHeight) + expectedTipHeaderHash := expectedTipHeader.BlockHash() + require.True(t, currentTip.Hash.ToChainhash().IsEqual(&expectedTipHeaderHash)) + require.True(t, currentTip.Header.Hash().ToChainhash().IsEqual(&expectedTipHeaderHash)) +} - if minHeight > maxHeight { - panic("Min height more than max height") +func chainToChainBytes(chain []*wire.BlockHeader) []bbn.BTCHeaderBytes { + chainBytes := make([]bbn.BTCHeaderBytes, len(chain)) + for i, header := range chain { + chainBytes[i] = bbn.NewBTCHeaderBytesFromBlockHeader(header) } + return chainBytes +} - tree.GenRandomBTCHeaderTree(r, minHeight, maxHeight, root, func(header *types.BTCHeaderInfo) bool { - err := k.InsertHeader(ctx, header.Header) - if err != nil { - panic(fmt.Sprintf("header insertion failed: %s", err)) - } - return true - }) +func chainWork(chain []*wire.BlockHeader) *sdkmath.Uint { + totalWork := sdkmath.NewUint(0) + for _, header := range chain { + totalWork = sdkmath.NewUintFromBigInt(new(big.Int).Add(totalWork.BigInt(), blockchain.CalcWork(header.Bits))) + } + return &totalWork } diff --git a/x/btclightclient/module.go b/x/btclightclient/module.go index ba1fd57be..6c6982d4a 100644 --- a/x/btclightclient/module.go +++ b/x/btclightclient/module.go @@ -2,6 +2,7 @@ package btclightclient import ( "context" + "cosmossdk.io/core/appmodule" "encoding/json" "fmt" @@ -22,8 +23,10 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // ---------------------------------------------------------------------------- @@ -98,22 +101,16 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { type AppModule struct { AppModuleBasic - keeper keeper.Keeper - accountKeeper types.AccountKeeper - bankKeeper types.BankKeeper + keeper keeper.Keeper } func NewAppModule( cdc codec.Codec, keeper keeper.Keeper, - accountKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, ) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), keeper: keeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, } } @@ -137,14 +134,12 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the btclightclient module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. @@ -157,10 +152,20 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(_ context.Context) error { + return nil +} // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. -func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - return []abci.ValidatorUpdate{} +func (am AppModule) EndBlock(_ context.Context) ([]abci.ValidatorUpdate, error) { + return []abci.ValidatorUpdate{}, nil +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker } diff --git a/x/btclightclient/module_simulation.go b/x/btclightclient/module_simulation.go deleted file mode 100644 index 1de351013..000000000 --- a/x/btclightclient/module_simulation.go +++ /dev/null @@ -1,47 +0,0 @@ -package btclightclient - -import ( - simappparams "github.com/babylonchain/babylon/app/params" - "github.com/babylonchain/babylon/testutil/sample" - btclightclientsimulation "github.com/babylonchain/babylon/x/btclightclient/simulation" - "github.com/babylonchain/babylon/x/btclightclient/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = sample.AccAddress - _ = btclightclientsimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - accs := make([]string, len(simState.Accounts)) - for i, acc := range simState.Accounts { - accs[i] = acc.Address.String() - } - btclightclientGenesis := types.DefaultGenesis() - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(btclightclientGenesis) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - operations := make([]simtypes.WeightedOperation, 0) - - return operations -} diff --git a/x/btclightclient/simulation/simap.go b/x/btclightclient/simulation/simap.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/btclightclient/simulation/simap.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/btclightclient/types/btc_header_info.go b/x/btclightclient/types/btc_header_info.go index baabc45a0..bb16a839c 100644 --- a/x/btclightclient/types/btc_header_info.go +++ b/x/btclightclient/types/btc_header_info.go @@ -21,7 +21,3 @@ func (m *BTCHeaderInfo) HasParent(parent *BTCHeaderInfo) bool { func (m *BTCHeaderInfo) Eq(other *BTCHeaderInfo) bool { return m.Hash.Eq(other.Hash) } - -func (m BTCHeaderInfo) Validate() error { - return nil -} diff --git a/x/btclightclient/types/btc_header_info_test.go b/x/btclightclient/types/btc_header_info_test.go index cd523563a..8a9e8d2ea 100644 --- a/x/btclightclient/types/btc_header_info_test.go +++ b/x/btclightclient/types/btc_header_info_test.go @@ -38,7 +38,7 @@ func FuzzNewHeaderInfo(f *testing.F) { gotHeaderBytes := headerInfo.Header.MustMarshal() if !bytes.Equal(expectedHeaderBytes.MustMarshal(), gotHeaderBytes) { - t.Errorf("Expected header %s got %s", expectedHeaderBytes, gotHeaderBytes) + t.Errorf("Expected header %v got %s", expectedHeaderBytes, gotHeaderBytes) } gotHashBytes := *headerInfo.Hash diff --git a/x/btclightclient/types/btc_light_client.go b/x/btclightclient/types/btc_light_client.go new file mode 100644 index 000000000..dbbd19204 --- /dev/null +++ b/x/btclightclient/types/btc_light_client.go @@ -0,0 +1,432 @@ +package types + +import ( + "fmt" + "time" + + sdkmath "cosmossdk.io/math" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" +) + +type BtcChainReadStore interface { + GetHeaderByHash(hash *bbn.BTCHeaderHashBytes) (*BTCHeaderInfo, error) + GetHeaderByHeight(height uint64) (*BTCHeaderInfo, error) + GetTip() *BTCHeaderInfo +} + +// Copy from neutrino light client +// https://github.com/lightninglabs/neutrino/blob/master/blockmanager.go#L2875 +type lightChainCtx struct { + params *chaincfg.Params + blocksPerRetarget int32 + minRetargetTimespan int64 + maxRetargetTimespan int64 +} + +var _ blockchain.ChainCtx = (*lightChainCtx)(nil) + +func newLightChainCtx(params *chaincfg.Params, blocksPerRetarget int32, + minRetargetTimespan, maxRetargetTimespan int64) *lightChainCtx { + + return &lightChainCtx{ + params: params, + blocksPerRetarget: blocksPerRetarget, + minRetargetTimespan: minRetargetTimespan, + maxRetargetTimespan: maxRetargetTimespan, + } +} + +func newLightChainCtxFromParams(params *chaincfg.Params) *lightChainCtx { + targetTimespan := int64(params.TargetTimespan / time.Second) + targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) + adjustmentFactor := params.RetargetAdjustmentFactor + + blocksPerRetarget := int32(targetTimespan / targetTimePerBlock) + minRetargetTimespan := targetTimespan / adjustmentFactor + maxRetargetTimespan := targetTimespan * adjustmentFactor + + return newLightChainCtx( + params, blocksPerRetarget, minRetargetTimespan, maxRetargetTimespan, + ) +} + +func (l *lightChainCtx) ChainParams() *chaincfg.Params { + return l.params +} + +func (l *lightChainCtx) BlocksPerRetarget() int32 { + return l.blocksPerRetarget +} + +func (l *lightChainCtx) MinRetargetTimespan() int64 { + return l.minRetargetTimespan +} + +func (l *lightChainCtx) MaxRetargetTimespan() int64 { + return l.maxRetargetTimespan +} + +// We never check checkpoints in our on-chain light client. Required by blockchain.ChainCtx interface +func (l *lightChainCtx) VerifyCheckpoint(int32, *chainhash.Hash) bool { + return false +} + +// If VerifyCheckpoint returns false, this function is never called. Required by blockchain.ChainCtx interface +func (l *lightChainCtx) FindPreviousCheckpoint() (blockchain.HeaderCtx, error) { + return nil, nil +} + +type localHeaderInfo struct { + header *wire.BlockHeader + height uint64 + totalWork sdkmath.Uint +} + +func newLocalHeaderInfo( + header *wire.BlockHeader, + height uint64, + totalWork sdkmath.Uint) *localHeaderInfo { + + return &localHeaderInfo{ + header: header, + height: height, + totalWork: totalWork, + } +} + +func toLocalInfo(i *BTCHeaderInfo) *localHeaderInfo { + if i == nil { + return nil + } + + return newLocalHeaderInfo(i.Header.ToBlockHeader(), i.Height, *i.Work) +} + +func (i *localHeaderInfo) toBTCHeaderInfo() *BTCHeaderInfo { + blockHash := i.header.BlockHash() + headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(i.header) + headerHash := bbn.NewBTCHeaderHashBytesFromChainhash(&blockHash) + + return NewBTCHeaderInfo( + &headerBytes, + &headerHash, + i.height, + &i.totalWork, + ) +} + +func toBTCHeaderInfos(infos []*localHeaderInfo) []*BTCHeaderInfo { + result := make([]*BTCHeaderInfo, len(infos)) + + for i, info := range infos { + result[i] = info.toBTCHeaderInfo() + } + + return result +} + +// based on neutrio light client +// https://github.com/lightninglabs/neutrino/blob/master/blockmanager.go#L2944 +type lightHeaderCtx struct { + height uint64 + bits uint32 + timestamp int64 + store *storeWithExtensionChain +} + +var _ blockchain.HeaderCtx = (*lightHeaderCtx)(nil) + +func newLightHeaderCtx(height uint64, header *wire.BlockHeader, + store *storeWithExtensionChain) *lightHeaderCtx { + + return &lightHeaderCtx{ + height: height, + bits: header.Bits, + timestamp: header.Timestamp.Unix(), + store: store, + } +} + +func (l *lightHeaderCtx) Height() int32 { + return int32(l.height) +} + +func (l *lightHeaderCtx) Bits() uint32 { + return l.bits +} + +func (l *lightHeaderCtx) Timestamp() int64 { + return l.timestamp +} + +func (l *lightHeaderCtx) Parent() blockchain.HeaderCtx { + // The parent is just an ancestor with distance 1. + anc := l.RelativeAncestorCtx(1) + + if anc == nil { + return nil + } + + return anc +} + +func (l *lightHeaderCtx) RelativeAncestorCtx( + distance int32) blockchain.HeaderCtx { + + ancestorHeight := l.Height() - distance + + if ancestorHeight < 0 { + // We don't have this header. + return nil + } + + ancU64 := uint64(ancestorHeight) + + ancestor := l.store.getHeaderAtHeight(ancU64) + + if ancestor == nil { + return nil + } + + return newLightHeaderCtx( + ancU64, ancestor.header, l.store, + ) +} + +type BtcLightClient struct { + params *chaincfg.Params + ctx *lightChainCtx +} + +func NewBtcLightClient( + params *chaincfg.Params, + ctx *lightChainCtx) *BtcLightClient { + return &BtcLightClient{ + params: params, + ctx: ctx, + } +} + +func NewBtcLightClientFromParams(params *chaincfg.Params) *BtcLightClient { + return NewBtcLightClient(params, newLightChainCtxFromParams(params)) +} + +func headersFormChain(headers []*wire.BlockHeader) bool { + var ( + lastHeader chainhash.Hash + emptyHash chainhash.Hash + ) + for _, blockHeader := range headers { + blockHash := blockHeader.BlockHash() + + // If we haven't yet set lastHeader, set it now. + if lastHeader == emptyHash { + lastHeader = blockHash + continue + } + + // Ensure that blockHeader.PrevBlock matches lastHeader. + if blockHeader.PrevBlock != lastHeader { + return false + } + + lastHeader = blockHash + } + + return true +} + +type DisableHeaderInTheFutureValidationTimeSource struct { + h *wire.BlockHeader +} + +func NewDisableHeaderInTheFutureValidationTimeSource(header *wire.BlockHeader) *DisableHeaderInTheFutureValidationTimeSource { + return &DisableHeaderInTheFutureValidationTimeSource{ + h: header, + } +} + +// AdjustedTime returns the current time adjusted by the median time +// offset as calculated from the time samples added by AddTimeSample. +func (d *DisableHeaderInTheFutureValidationTimeSource) AdjustedTime() time.Time { + return d.h.Timestamp +} + +func (d *DisableHeaderInTheFutureValidationTimeSource) AddTimeSample(_ string, _ time.Time) { + //no op +} + +func (d *DisableHeaderInTheFutureValidationTimeSource) Offset() time.Duration { + return 0 * time.Second +} + +type storeWithExtensionChain struct { + headers []*localHeaderInfo + store BtcChainReadStore +} + +func newStoreWithExtensionChain( + store BtcChainReadStore, + maxExentsionHeaders int, +) *storeWithExtensionChain { + + return &storeWithExtensionChain{ + // large capacity to avoid reallocation + headers: make([]*localHeaderInfo, 0, maxExentsionHeaders), + store: store, + } +} + +func (s *storeWithExtensionChain) addHeader(header *localHeaderInfo) { + s.headers = append(s.headers, header) +} + +func (s *storeWithExtensionChain) getHeaderAtHeight(height uint64) *localHeaderInfo { + if len(s.headers) == 0 || height < s.headers[0].height { + h, err := s.store.GetHeaderByHeight(height) + + if err != nil { + return nil + } + return newLocalHeaderInfo(h.Header.ToBlockHeader(), height, *h.Work) + } else { + headerIndex := height - s.headers[0].height + return s.headers[headerIndex] + } +} + +func (l *BtcLightClient) processNewHeadersChain( + store *storeWithExtensionChain, + chainParent *localHeaderInfo, + chain []*wire.BlockHeader) error { + // init info about parent as current tip + parentHeaderInfo := chainParent + + for _, blockHeader := range chain { + h := blockHeader + + err := l.checkHeader( + store, parentHeaderInfo, h, + ) + + if err != nil { + return fmt.Errorf("provided header contains invalid header. Error msg: %s: %w", err.Error(), ErrInvalidHeader) + } + + childWork := CalcHeaderWork(h) + newHeaderInfo := newLocalHeaderInfo( + h, + parentHeaderInfo.height+1, + CumulativeWork(parentHeaderInfo.totalWork, childWork), + ) + store.addHeader(newHeaderInfo) + parentHeaderInfo = newHeaderInfo + } + + return nil +} + +type RollbackInfo struct { + HeaderToRollbackTo *BTCHeaderInfo +} + +type InsertResult struct { + HeadersToInsert []*BTCHeaderInfo + // if rollback is not nil, it means that we need to rollback to the provided header + RollbackInfo *RollbackInfo +} + +func (l *BtcLightClient) InsertHeaders(readStore BtcChainReadStore, headers []*wire.BlockHeader) (*InsertResult, error) { + headersLen := len(headers) + if headersLen == 0 { + return nil, fmt.Errorf("cannot insert empty headers") + } + + if !headersFormChain(headers) { + return nil, fmt.Errorf("headers do not form a chain") + } + + currentTip := toLocalInfo(readStore.GetTip()) + + if currentTip == nil { + return nil, fmt.Errorf("cannot insert headers when tip is nil") + } + + currentTipHash := currentTip.header.BlockHash() + + firstHeaderOfExtensionChain := headers[0] + + store := newStoreWithExtensionChain(readStore, headersLen) + + if firstHeaderOfExtensionChain.PrevBlock.IsEqual(¤tTipHash) { + // most common case - extending of current tip + if err := l.processNewHeadersChain(store, currentTip, headers); err != nil { + return nil, err + } + + return &InsertResult{ + HeadersToInsert: toBTCHeaderInfos(store.headers), + RollbackInfo: nil, + }, nil + } else { + // here we received potential new fork + parentHash := bbn.NewBTCHeaderHashBytesFromChainhash(&firstHeaderOfExtensionChain.PrevBlock) + forkParent, err := readStore.GetHeaderByHash(&parentHash) + + if err != nil { + return nil, fmt.Errorf("cannot find parent header with hash %s for provided chain: %w", parentHash.String(), ErrHeaderParentDoesNotExist) + } + + forkParentInfo := toLocalInfo(forkParent) + + if err := l.processNewHeadersChain(store, forkParentInfo, headers); err != nil { + return nil, err + } + + tipOfNewChain := store.headers[len(store.headers)-1] + + if tipOfNewChain.totalWork.LTE(currentTip.totalWork) { + return nil, fmt.Errorf("new chain work %s, is not better than current tip work %s: %w", + tipOfNewChain.totalWork.String(), currentTip.totalWork.String(), ErrChainWithNotEnoughWork) + } + + return &InsertResult{ + HeadersToInsert: toBTCHeaderInfos(store.headers), + RollbackInfo: &RollbackInfo{ + // we need to rollback to fork parent + HeaderToRollbackTo: forkParent, + }, + }, nil + } +} + +// checkHeader checks if the header is valid and can be added to the store. +// One criticial condition is that to properly validate difficulty adjustments +// we should have at least one header which is at difficulty adjustment boundary +// in store. +func (l *BtcLightClient) checkHeader( + s *storeWithExtensionChain, + parentHeaderInfo *localHeaderInfo, + blockHeader *wire.BlockHeader, +) error { + parentHeaderCtx := newLightHeaderCtx( + parentHeaderInfo.height, parentHeaderInfo.header, s, + ) + + var emptyFlags blockchain.BehaviorFlags + err := blockchain.CheckBlockHeaderContext( + blockHeader, parentHeaderCtx, emptyFlags, l.ctx, true, + ) + if err != nil { + return err + } + + return blockchain.CheckBlockHeaderSanity( + blockHeader, l.params.PowLimit, NewDisableHeaderInTheFutureValidationTimeSource(blockHeader), + emptyFlags, + ) +} diff --git a/x/btclightclient/types/btclightclient.pb.go b/x/btclightclient/types/btclightclient.pb.go index d4dc5f187..dd50ff83e 100644 --- a/x/btclightclient/types/btclightclient.pb.go +++ b/x/btclightclient/types/btclightclient.pb.go @@ -4,9 +4,9 @@ package types import ( + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -37,7 +37,7 @@ type BTCHeaderInfo struct { Header *github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,1,opt,name=header,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"header,omitempty"` Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` - Work *github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,4,opt,name=work,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"work,omitempty"` + Work *cosmossdk_io_math.Uint `protobuf:"bytes,4,opt,name=work,proto3,customtype=cosmossdk.io/math.Uint" json:"work,omitempty"` } func (m *BTCHeaderInfo) Reset() { *m = BTCHeaderInfo{} } @@ -89,25 +89,25 @@ func init() { } var fileDescriptor_84bf438d909b681d = []byte{ - // 279 bytes of a gzipped FileDescriptorProto + // 282 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4b, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0x65, 0x86, 0x68, 0x22, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0xf5, 0x7a, 0x68, 0xb2, 0x65, 0x86, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x55, 0xfa, 0x20, 0x16, 0x44, - 0x83, 0x52, 0x0f, 0x13, 0x17, 0xaf, 0x53, 0x88, 0xb3, 0x47, 0x6a, 0x62, 0x4a, 0x6a, 0x91, 0x67, - 0x5e, 0x5a, 0xbe, 0x50, 0x00, 0x17, 0x5b, 0x06, 0x98, 0x27, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe3, - 0x64, 0x71, 0xeb, 0x9e, 0xbc, 0x49, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, - 0x3e, 0xd4, 0x86, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x18, 0x47, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x58, - 0x0f, 0x6e, 0x90, 0x53, 0x65, 0x49, 0x6a, 0x71, 0x10, 0xd4, 0x1c, 0xa1, 0x00, 0x2e, 0x96, 0x8c, - 0xc4, 0xe2, 0x0c, 0x09, 0x26, 0xb0, 0x79, 0x36, 0xb7, 0xee, 0xc9, 0x5b, 0x90, 0x68, 0x9e, 0x47, - 0x62, 0x71, 0x06, 0xc4, 0x4c, 0xb0, 0x49, 0x42, 0x62, 0x20, 0x37, 0x82, 0xbc, 0x27, 0xc1, 0xac, - 0xc0, 0xa8, 0xc1, 0x12, 0x04, 0xe5, 0x09, 0xd9, 0x73, 0xb1, 0x94, 0xe7, 0x17, 0x65, 0x4b, 0xb0, - 0x80, 0x6d, 0xd2, 0xbe, 0x75, 0x4f, 0x5e, 0x1d, 0xc9, 0xa6, 0xe4, 0xfc, 0xe2, 0xdc, 0xfc, 0x62, - 0x28, 0xa5, 0x5b, 0x9c, 0x92, 0x0d, 0xb5, 0x26, 0x34, 0x33, 0xaf, 0x24, 0x08, 0xac, 0xd1, 0x29, - 0xe0, 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, 0xcc, 0x08, 0x39, 0xb9, 0x02, - 0x3d, 0x8e, 0xc0, 0x86, 0x27, 0xb1, 0x81, 0xc3, 0xd9, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x8a, - 0x8d, 0x9c, 0x25, 0xca, 0x01, 0x00, 0x00, + 0x83, 0xd2, 0x6f, 0x46, 0x2e, 0x5e, 0xa7, 0x10, 0x67, 0x8f, 0xd4, 0xc4, 0x94, 0xd4, 0x22, 0xcf, + 0xbc, 0xb4, 0x7c, 0xa1, 0x00, 0x2e, 0xb6, 0x0c, 0x30, 0x4f, 0x82, 0x51, 0x81, 0x51, 0x83, 0xc7, + 0xc9, 0xe2, 0xd6, 0x3d, 0x79, 0x93, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, + 0x7d, 0xa8, 0x0d, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x49, 0x65, 0x41, 0x6a, 0xb1, + 0x1e, 0xdc, 0x20, 0xa7, 0xca, 0x92, 0xd4, 0xe2, 0x20, 0xa8, 0x39, 0x42, 0x01, 0x5c, 0x2c, 0x19, + 0x89, 0xc5, 0x19, 0x12, 0x4c, 0x60, 0xf3, 0x6c, 0x6e, 0xdd, 0x93, 0xb7, 0x20, 0xd1, 0x3c, 0x8f, + 0xc4, 0xe2, 0x0c, 0x88, 0x99, 0x60, 0x93, 0x84, 0xc4, 0x40, 0x6e, 0x04, 0x79, 0x4f, 0x82, 0x59, + 0x81, 0x51, 0x83, 0x25, 0x08, 0xca, 0x13, 0xd2, 0xe3, 0x62, 0x29, 0xcf, 0x2f, 0xca, 0x96, 0x60, + 0x01, 0xdb, 0x24, 0x75, 0xeb, 0x9e, 0xbc, 0x58, 0x72, 0x7e, 0x71, 0x6e, 0x7e, 0x71, 0x71, 0x4a, + 0xb6, 0x5e, 0x66, 0xbe, 0x7e, 0x6e, 0x62, 0x49, 0x86, 0x5e, 0x68, 0x66, 0x5e, 0x49, 0x10, 0x58, + 0x9d, 0x53, 0xc0, 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, 0x11, 0x72, + 0x61, 0x05, 0x7a, 0x94, 0x80, 0x9d, 0x9c, 0xc4, 0x06, 0x0e, 0x56, 0x63, 0x40, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x13, 0xc6, 0x69, 0x40, 0xb9, 0x01, 0x00, 0x00, } func (m *BTCHeaderInfo) Marshal() (dAtA []byte, err error) { @@ -362,7 +362,7 @@ func (m *BTCHeaderInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_cosmos_cosmos_sdk_types.Uint + var v cosmossdk_io_math.Uint m.Work = &v if err := m.Work.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err diff --git a/x/btclightclient/types/codec.go b/x/btclightclient/types/codec.go index 3a094f1e8..c1e243974 100644 --- a/x/btclightclient/types/codec.go +++ b/x/btclightclient/types/codec.go @@ -7,14 +7,14 @@ import ( "github.com/cosmos/cosmos-sdk/types/msgservice" ) -func RegisterCodec(cdc *codec.LegacyAmino) { +func RegisterCodec(_ *codec.LegacyAmino) { } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { - // Register messages + // Register messages registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgInsertHeader{}, + &MsgInsertHeaders{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/x/btclightclient/types/errors.go b/x/btclightclient/types/errors.go index 4ff8ee786..f9c694c38 100644 --- a/x/btclightclient/types/errors.go +++ b/x/btclightclient/types/errors.go @@ -9,9 +9,11 @@ import ( // x/btclightclient module sentinel errors var ( ErrHeaderDoesNotExist = errorsmod.Register(ModuleName, 1100, "header does not exist") - ErrDuplicateHeader = errorsmod.Register(ModuleName, 1101, "header with provided hash already exists") - ErrHeaderParentDoesNotExist = errorsmod.Register(ModuleName, 1102, "parent for provided hash is not maintained") - ErrInvalidDifficulty = errorsmod.Register(ModuleName, 1103, "invalid difficulty bits") - ErrEmptyMessage = errorsmod.Register(ModuleName, 1104, "empty message provided") - ErrInvalidProofOfWOrk = errorsmod.Register(ModuleName, 1105, "provided header has invalid proof of work") + ErrHeaderParentDoesNotExist = errorsmod.Register(ModuleName, 1101, "parent for provided hash is not maintained") + ErrEmptyMessage = errorsmod.Register(ModuleName, 1102, "empty message provided") + ErrInvalidProofOfWOrk = errorsmod.Register(ModuleName, 1103, "provided header has invalid proof of work") + ErrInvalidHeader = errorsmod.Register(ModuleName, 1104, "provided header does not satisfy header validation rules") + ErrChainWithNotEnoughWork = errorsmod.Register(ModuleName, 1105, "provided chain has not enough work") + ErrUnauthorizedReporter = errorsmod.Register(ModuleName, 1106, "unauthorized reporter") + ErrInvalidMessageFormat = errorsmod.Register(ModuleName, 1107, "invalid message format") ) diff --git a/x/btclightclient/types/expected_keepers.go b/x/btclightclient/types/expected_keepers.go index 3db963c48..387b50c76 100644 --- a/x/btclightclient/types/expected_keepers.go +++ b/x/btclightclient/types/expected_keepers.go @@ -1,24 +1,11 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" + "context" ) -// AccountKeeper defines the expected account keeper used for simulations (noalias) -type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI - // Methods imported from account should be defined here -} - -// BankKeeper defines the expected interface needed to retrieve account balances. -type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here -} - type BTCLightClientHooks interface { - AfterBTCRollBack(ctx sdk.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled back - AfterBTCRollForward(ctx sdk.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled forward - AfterBTCHeaderInserted(ctx sdk.Context, headerInfo *BTCHeaderInfo) // Must be called after a header is inserted + AfterBTCRollBack(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled back + AfterBTCRollForward(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled forward + AfterBTCHeaderInserted(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after a header is inserted } diff --git a/x/btclightclient/types/genesis.go b/x/btclightclient/types/genesis.go index f9e5226f9..2f5300632 100644 --- a/x/btclightclient/types/genesis.go +++ b/x/btclightclient/types/genesis.go @@ -1,33 +1,56 @@ package types import ( + "fmt" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/chaincfg" ) +func SimnetGenesisBlock() BTCHeaderInfo { + // By default we use the genesis block of the simnet, as it is the best for testing + var header = chaincfg.SimNetParams.GenesisBlock.Header + var headerHash = chaincfg.SimNetParams.GenesisHash + + bytes := bbn.NewBTCHeaderBytesFromBlockHeader(&header) + hash := bbn.NewBTCHeaderHashBytesFromChainhash(headerHash) + work := CalcWork(&bytes) + + return *NewBTCHeaderInfo( + &bytes, + &hash, + 0, + &work, + ) +} + // DefaultGenesis returns the default Capability genesis state func DefaultGenesis() *GenesisState { - headerBytes := bbn.GetBaseBTCHeaderBytes() - headerHeight := bbn.GetBaseBTCHeaderHeight() - headerHash := headerBytes.Hash() - // The cumulative work for the Base BTC header is only the work - // for that particular header. This means that it is very important - // that no forks will happen that discard the base header because we - // will not be able to detect those. Cumulative work will build based - // on the sum of the work of the chain starting from the base header. - headerWork := CalcWork(&headerBytes) - baseHeaderInfo := NewBTCHeaderInfo(&headerBytes, headerHash, headerHeight, &headerWork) + defaultBaseHeader := SimnetGenesisBlock() return &GenesisState{ - BaseBtcHeader: *baseHeaderInfo, + BaseBtcHeader: defaultBaseHeader, + Params: DefaultParams(), } } // Validate performs basic genesis state validation returning an error upon any // failure. func (gs GenesisState) Validate() error { - err := gs.BaseBtcHeader.Validate() - if err != nil { - return err + // We Require that genesis block is difficulty adjustment block, so that we can + // properly calculate the difficulty adjustments in the future. + // TODO: Even though number of block per re-target depends on the network, in reality it + // is always 2016. Maybe we should consider moving it to param, or try to pass + // it through + isRetarget := IsRetargetBlock(&gs.BaseBtcHeader, &chaincfg.MainNetParams) + + if !isRetarget { + return fmt.Errorf("genesis block must be a difficulty adjustment block") + } + + if err := gs.Params.Validate(); err != nil { + return fmt.Errorf("invalid params in genesis: %w", err) } + return nil } diff --git a/x/btclightclient/types/genesis.pb.go b/x/btclightclient/types/genesis.pb.go index 096396119..d3cc8819a 100644 --- a/x/btclightclient/types/genesis.pb.go +++ b/x/btclightclient/types/genesis.pb.go @@ -25,7 +25,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the btclightclient module's genesis state. type GenesisState struct { - BaseBtcHeader BTCHeaderInfo `protobuf:"bytes,1,opt,name=base_btc_header,json=baseBtcHeader,proto3" json:"base_btc_header"` + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + BaseBtcHeader BTCHeaderInfo `protobuf:"bytes,2,opt,name=base_btc_header,json=baseBtcHeader,proto3" json:"base_btc_header"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -61,6 +62,13 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + func (m *GenesisState) GetBaseBtcHeader() BTCHeaderInfo { if m != nil { return m.BaseBtcHeader @@ -77,22 +85,24 @@ func init() { } var fileDescriptor_4f95902e4096217a = []byte{ - // 227 bytes of a gzipped FileDescriptorProto + // 260 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4f, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0x65, 0x86, 0xfa, 0xe9, 0xa9, 0x79, 0xa9, 0xc5, 0x99, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x85, 0x7a, 0xa8, 0x0a, 0xf5, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, - 0xf3, 0xc1, 0xaa, 0xf4, 0x41, 0x2c, 0x88, 0x06, 0x29, 0x3d, 0xdc, 0x26, 0xa3, 0x19, 0x01, 0x56, - 0xaf, 0x94, 0xc6, 0xc5, 0xe3, 0x0e, 0xb1, 0x31, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0x28, 0x8c, 0x8b, - 0x3f, 0x29, 0xb1, 0x38, 0x35, 0x3e, 0xa9, 0x24, 0x39, 0x3e, 0x23, 0x35, 0x31, 0x25, 0xb5, 0x48, - 0x82, 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x43, 0x0f, 0xa7, 0x53, 0xf4, 0x9c, 0x42, 0x9c, 0x3d, - 0xc0, 0x6a, 0x3d, 0xf3, 0xd2, 0xf2, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0xe2, 0x05, 0x19, - 0xe3, 0x54, 0x92, 0x0c, 0x91, 0x70, 0x0a, 0x38, 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, 0xb3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0x15, - 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x05, 0xba, 0x5f, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, - 0x93, 0xd8, 0xc0, 0x1e, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x13, 0xa2, 0xce, 0xd4, 0x4c, - 0x01, 0x00, 0x00, + 0xf3, 0xc1, 0xaa, 0xf4, 0x41, 0x2c, 0x88, 0x06, 0x29, 0x3d, 0xdc, 0x26, 0xa3, 0x19, 0x01, 0x51, + 0xaf, 0x86, 0x5b, 0x7d, 0x41, 0x62, 0x51, 0x62, 0x2e, 0xd4, 0x21, 0x4a, 0xcb, 0x19, 0xb9, 0x78, + 0xdc, 0x21, 0x4e, 0x0b, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0xb2, 0xe7, 0x62, 0x83, 0x28, 0x90, 0x60, + 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd4, 0xc3, 0xe9, 0x54, 0xbd, 0x00, 0xb0, 0x42, 0x27, 0x96, + 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0xda, 0x84, 0xc2, 0xb8, 0xf8, 0x93, 0x12, 0x8b, 0x53, 0xe3, + 0x93, 0x4a, 0x92, 0xe3, 0x33, 0x52, 0x13, 0x53, 0x52, 0x8b, 0x24, 0x98, 0xc0, 0x26, 0x69, 0xe0, + 0x31, 0xc9, 0x29, 0xc4, 0xd9, 0x03, 0xac, 0xd6, 0x33, 0x2f, 0x2d, 0x1f, 0x6a, 0x20, 0x2f, 0xc8, + 0x18, 0xa7, 0x92, 0x64, 0x88, 0x84, 0x53, 0xc0, 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, 0xa5, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0xad, + 0x48, 0xce, 0x48, 0xcc, 0xcc, 0x83, 0x71, 0xf4, 0x2b, 0xd0, 0x43, 0xa1, 0xa4, 0xb2, 0x20, 0xb5, + 0x38, 0x89, 0x0d, 0x1c, 0x04, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x0c, 0x15, 0x34, + 0xb6, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -124,6 +134,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- + dAtA[i] = 0x12 + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -145,6 +165,8 @@ func (m *GenesisState) Size() (n int) { } var l int _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) l = m.BaseBtcHeader.Size() n += 1 + l + sovGenesis(uint64(l)) return n @@ -186,6 +208,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { } 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 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 + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BaseBtcHeader", wireType) } diff --git a/x/btclightclient/types/hooks.go b/x/btclightclient/types/hooks.go index a2d2af000..779bfc7ca 100644 --- a/x/btclightclient/types/hooks.go +++ b/x/btclightclient/types/hooks.go @@ -1,7 +1,7 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "context" ) var _ BTCLightClientHooks = &MultiBTCLightClientHooks{} @@ -12,19 +12,19 @@ func NewMultiBTCLightClientHooks(hooks ...BTCLightClientHooks) MultiBTCLightClie return hooks } -func (h MultiBTCLightClientHooks) AfterBTCHeaderInserted(ctx sdk.Context, headerInfo *BTCHeaderInfo) { +func (h MultiBTCLightClientHooks) AfterBTCHeaderInserted(ctx context.Context, headerInfo *BTCHeaderInfo) { for i := range h { h[i].AfterBTCHeaderInserted(ctx, headerInfo) } } -func (h MultiBTCLightClientHooks) AfterBTCRollBack(ctx sdk.Context, headerInfo *BTCHeaderInfo) { +func (h MultiBTCLightClientHooks) AfterBTCRollBack(ctx context.Context, headerInfo *BTCHeaderInfo) { for i := range h { h[i].AfterBTCRollBack(ctx, headerInfo) } } -func (h MultiBTCLightClientHooks) AfterBTCRollForward(ctx sdk.Context, headerInfo *BTCHeaderInfo) { +func (h MultiBTCLightClientHooks) AfterBTCRollForward(ctx context.Context, headerInfo *BTCHeaderInfo) { for i := range h { h[i].AfterBTCRollForward(ctx, headerInfo) } diff --git a/x/btclightclient/types/keys.go b/x/btclightclient/types/keys.go index 4203eaf9b..b0844f440 100644 --- a/x/btclightclient/types/keys.go +++ b/x/btclightclient/types/keys.go @@ -23,36 +23,15 @@ const ( ) var ( - HeadersPrefix = []byte{0x0} // reserve this namespace for headers - HeadersObjectPrefix = append(HeadersPrefix, 0x0) // where we save the concrete header bytes - HashToHeightPrefix = append(HeadersPrefix, 0x1) // where we map hash to height - HashToWorkPrefix = append(HeadersPrefix, 0x2) // where we map hash to height - TipPrefix = append(HeadersPrefix, 0x3) // where we store the tip + HeadersObjectPrefix = []byte{0x01} // reserve this namespace mapping: Height -> BTCHeaderInfo + HashToHeightPrefix = []byte{0x02} // reserve this namespace mapping: Hash -> Height + ParamsKey = []byte{0x03} // key for params ) -func HeadersObjectKey(height uint64, hash *bbn.BTCHeaderHashBytes) []byte { - he := sdk.Uint64ToBigEndian(height) - hashBytes := hash.MustMarshal() - - var prefix []byte - prefix = append(prefix, he...) - return append(prefix, hashBytes...) +func HeadersObjectKey(height uint64) []byte { + return sdk.Uint64ToBigEndian(height) } func HeadersObjectHeightKey(hash *bbn.BTCHeaderHashBytes) []byte { - var prefix []byte - return append(prefix, hash.MustMarshal()...) -} - -func HeadersObjectWorkKey(hash *bbn.BTCHeaderHashBytes) []byte { - var prefix []byte - return append(prefix, hash.MustMarshal()...) -} - -func TipKey() []byte { - return TipPrefix -} - -func KeyPrefix(p string) []byte { - return []byte(p) + return hash.MustMarshal() } diff --git a/x/btclightclient/types/keys_test.go b/x/btclightclient/types/keys_test.go index 7c1530abf..e075a6c4e 100644 --- a/x/btclightclient/types/keys_test.go +++ b/x/btclightclient/types/keys_test.go @@ -16,19 +16,14 @@ func FuzzHeadersObjectKey(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - hexHash := datagen.GenRandomHexStr(r, bbn.BTCHeaderHashLen) height := r.Uint64() // get chainhash and height heightBytes := sdk.Uint64ToBigEndian(height) - headerHash, _ := bbn.NewBTCHeaderHashBytesFromHex(hexHash) - // construct the expected key - headerHashBytes := headerHash.MustMarshal() var expectedKey []byte expectedKey = append(expectedKey, heightBytes...) - expectedKey = append(expectedKey, headerHashBytes...) - gotKey := types.HeadersObjectKey(height, &headerHash) + gotKey := types.HeadersObjectKey(height) if !bytes.Equal(expectedKey, gotKey) { t.Errorf("Expected headers object key %s got %s", expectedKey, gotKey) } @@ -50,18 +45,5 @@ func FuzzHeadersObjectHeightAndWorkKey(f *testing.F) { if !bytes.Equal(expectedHeightKey, gotHeightKey) { t.Errorf("Expected headers object height key %s got %s", expectedHeightKey, gotHeightKey) } - - var expectedWorkKey []byte - expectedWorkKey = append(expectedWorkKey, headerHashBytes...) - gotWorkKey := types.HeadersObjectWorkKey(&headerHash) - if !bytes.Equal(expectedWorkKey, gotWorkKey) { - t.Errorf("Expected headers object work key %s got %s", expectedWorkKey, gotWorkKey) - } }) } - -func TestTipKey(t *testing.T) { - if !bytes.Equal(types.TipKey(), types.TipPrefix) { - t.Errorf("Expected tip key %s got %s", types.TipKey(), types.TipPrefix) - } -} diff --git a/x/btclightclient/types/msgs.go b/x/btclightclient/types/msgs.go index 6c33c1c56..038b9ac8c 100644 --- a/x/btclightclient/types/msgs.go +++ b/x/btclightclient/types/msgs.go @@ -1,44 +1,74 @@ package types import ( + "encoding/hex" + "fmt" "math/big" bbn "github.com/babylonchain/babylon/types" sdk "github.com/cosmos/cosmos-sdk/types" ) -// Ensure that MsgInsertHeader implements all functions of the Msg interface -var _ sdk.Msg = (*MsgInsertHeader)(nil) +var _ sdk.Msg = (*MsgInsertHeaders)(nil) + +func NewMsgInsertHeaders(signer sdk.AccAddress, headersHex string) (*MsgInsertHeaders, error) { + if len(headersHex) == 0 { + return nil, fmt.Errorf("empty headers list") + } + + decoded, err := hex.DecodeString(headersHex) -func NewMsgInsertHeader(signer sdk.AccAddress, headerHex string) (*MsgInsertHeader, error) { - headerBytes, err := bbn.NewBTCHeaderBytesFromHex(headerHex) if err != nil { return nil, err } - return &MsgInsertHeader{Signer: signer.String(), Header: &headerBytes}, nil + + if len(decoded)%bbn.BTCHeaderLen != 0 { + return nil, fmt.Errorf("invalid length of encoded headers: %d", len(decoded)) + } + numOfHeaders := len(decoded) / bbn.BTCHeaderLen + headers := make([]bbn.BTCHeaderBytes, numOfHeaders) + + for i := 0; i < numOfHeaders; i++ { + headerSlice := decoded[i*bbn.BTCHeaderLen : (i+1)*bbn.BTCHeaderLen] + headerBytes, err := bbn.NewBTCHeaderBytesFromBytes(headerSlice) + if err != nil { + return nil, err + } + headers[i] = headerBytes + } + return &MsgInsertHeaders{Signer: signer.String(), Headers: headers}, nil } -func (msg *MsgInsertHeader) ValidateBasic() error { - // This function validates stateless message elements - // msg.Header is validated in ante-handler - _, err := sdk.AccAddressFromBech32(msg.Signer) - if err != nil { - return err +func (msg *MsgInsertHeaders) ValidateHeaders(powLimit *big.Int) error { + // TOOD: Limit number of headers in message? + for _, header := range msg.Headers { + err := bbn.ValidateBTCHeader(header.ToBlockHeader(), powLimit) + if err != nil { + return err + } } + return nil } -func (msg *MsgInsertHeader) ValidateHeader(powLimit *big.Int) error { - return bbn.ValidateBTCHeader(msg.Header.ToBlockHeader(), powLimit) +func (msg *MsgInsertHeaders) ReporterAddress() sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return sender } -func (msg *MsgInsertHeader) GetSigners() []sdk.AccAddress { - signer, err := sdk.AccAddressFromBech32(msg.Signer) +func (msg *MsgInsertHeaders) ValidateStateless() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { - // Panic, since the GetSigners method is called after ValidateBasic - // which performs the same check. - panic(err) + return err } - return []sdk.AccAddress{signer} + if len(msg.Headers) == 0 { + return fmt.Errorf("empty headers list") + } + + return nil } diff --git a/x/btclightclient/types/msgs_test.go b/x/btclightclient/types/msgs_test.go index 27641d364..a55240fa0 100644 --- a/x/btclightclient/types/msgs_test.go +++ b/x/btclightclient/types/msgs_test.go @@ -11,6 +11,7 @@ import ( bbn "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) func FuzzMsgInsertHeader(f *testing.F) { @@ -28,7 +29,8 @@ func FuzzMsgInsertHeader(f *testing.F) { // Get the signer structure var signer sdk.AccAddress - signer.Unmarshal(addressBytes) //nolint:errcheck // this is a test + err := signer.Unmarshal(addressBytes) + require.NoError(t, err) // Perform modifications on the header errorKind = r.Intn(2) @@ -40,7 +42,7 @@ func FuzzMsgInsertHeader(f *testing.F) { bitsBig = sdkmath.NewUintFromBigInt(&maxDifficulty) case 1: // Zero PoW - bitsBig = sdk.NewUint(0) + bitsBig = sdkmath.NewUint(0) default: bitsBig = sdkmath.NewUintFromBigInt(&maxDifficulty) } @@ -58,23 +60,41 @@ func FuzzMsgInsertHeader(f *testing.F) { t.Skip() } + numHeaders := r.Intn(20) + 1 + headersHex := "" + for i := 0; i < numHeaders; i++ { + headersHex += newHeaderHex + } + + // empty string + _, err = types.NewMsgInsertHeaders(signer, "") + require.NotNil(t, err) + + // hex string with invalid length + invalidLength := uint64(r.Int31n(79) + 1) + _, err = types.NewMsgInsertHeaders(signer, headersHex+datagen.GenRandomHexStr(r, invalidLength)) + require.NotNil(t, err) + // Check the message creation - msgInsertHeader, err := types.NewMsgInsertHeader(signer, newHeaderHex) + msgInsertHeader, err := types.NewMsgInsertHeaders(signer, headersHex) if err != nil { t.Errorf("Valid parameters led to error") } if msgInsertHeader == nil { t.Fatalf("nil returned") } - if msgInsertHeader.Header == nil { - t.Errorf("nil header") + if msgInsertHeader.Headers == nil || len(msgInsertHeader.Headers) != numHeaders { + t.Errorf("invalid number of headers") } - if !bytes.Equal(newHeader.MustMarshal(), msgInsertHeader.Header.MustMarshal()) { - t.Errorf("Expected header bytes %s got %s", newHeader.MustMarshal(), msgInsertHeader.Header.MustMarshal()) + + for _, header := range msgInsertHeader.Headers { + if !bytes.Equal(newHeader.MustMarshal(), header.MustMarshal()) { + t.Errorf("Expected header bytes %s got %s", newHeader.MustMarshal(), header.MustMarshal()) + } } // Validate the message - err = msgInsertHeader.ValidateHeader(&maxDifficulty) + err = msgInsertHeader.ValidateHeaders(&maxDifficulty) if err != nil && errorKind == 0 { t.Errorf("Valid message %s failed with %s", headerHex, err) } diff --git a/x/btclightclient/types/params.go b/x/btclightclient/types/params.go new file mode 100644 index 000000000..8b07a206a --- /dev/null +++ b/x/btclightclient/types/params.go @@ -0,0 +1,58 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewParams creates a new Params instance +func NewParams(allowedAddresses []string) Params { + return Params{ + InsertHeadersAllowList: allowedAddresses, + } +} + +func NewParamsValidate(allowedAddresses []string) (Params, error) { + p := NewParams(allowedAddresses) + if err := p.Validate(); err != nil { + return Params{}, err + } + return p, nil +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return NewParams( + []string{}, + ) +} + +func ValidateAddressList(i interface{}) error { + allowList, ok := i.([]string) + + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for _, a := range allowList { + if _, err := sdk.AccAddressFromBech32(a); err != nil { + return fmt.Errorf("invalid address") + } + } + + return nil +} + +// Validate validates the set of params +func (p Params) Validate() error { + if err := ValidateAddressList(p.InsertHeadersAllowList); err != nil { + return err + } + + return nil +} + +func (p *Params) AllowAllReporters() bool { + return len(p.InsertHeadersAllowList) == 0 +} diff --git a/x/btclightclient/types/params.pb.go b/x/btclightclient/types/params.pb.go new file mode 100644 index 000000000..bdd917254 --- /dev/null +++ b/x/btclightclient/types/params.pb.go @@ -0,0 +1,357 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btclightclient/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 { + // List of addresses which are allowed to insert headers to btc light client + // if the list is empty, any address can insert headers + InsertHeadersAllowList []string `protobuf:"bytes,1,rep,name=insert_headers_allow_list,json=insertHeadersAllowList,proto3" json:"insert_headers_allow_list,omitempty"` +} + +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_1e4c5f7a17079e1f, []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) GetInsertHeadersAllowList() []string { + if m != nil { + return m.InsertHeadersAllowList + } + return nil +} + +func init() { + proto.RegisterType((*Params)(nil), "babylon.btclightclient.v1.Params") +} + +func init() { + proto.RegisterFile("babylon/btclightclient/v1/params.proto", fileDescriptor_1e4c5f7a17079e1f) +} + +var fileDescriptor_1e4c5f7a17079e1f = []byte{ + // 211 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, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, + 0xfa, 0x65, 0x86, 0xfa, 0x05, 0x89, 0x45, 0x89, 0xb9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, + 0x42, 0x92, 0x50, 0x75, 0x7a, 0xa8, 0xea, 0xf4, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, + 0xc1, 0xaa, 0xf4, 0x41, 0x2c, 0x88, 0x06, 0x25, 0x4f, 0x2e, 0xb6, 0x00, 0xb0, 0x01, 0x42, 0x96, + 0x5c, 0x92, 0x99, 0x79, 0xc5, 0xa9, 0x45, 0x25, 0xf1, 0x19, 0xa9, 0x89, 0x29, 0xa9, 0x45, 0xc5, + 0xf1, 0x89, 0x39, 0x39, 0xf9, 0xe5, 0xf1, 0x39, 0x99, 0xc5, 0x25, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, + 0x9c, 0x41, 0x62, 0x10, 0x05, 0x1e, 0x10, 0x79, 0x47, 0x90, 0xb4, 0x4f, 0x66, 0x71, 0x89, 0x15, + 0xcb, 0x8b, 0x05, 0xf2, 0x8c, 0x4e, 0x01, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, + 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, + 0x10, 0x65, 0x96, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x75, 0x60, + 0x72, 0x46, 0x62, 0x66, 0x1e, 0x8c, 0xa3, 0x5f, 0x81, 0xee, 0xaf, 0x92, 0xca, 0x82, 0xd4, 0xe2, + 0x24, 0x36, 0xb0, 0x1b, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x30, 0xe4, 0x07, 0x29, 0xfe, + 0x00, 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 len(this.InsertHeadersAllowList) != len(that1.InsertHeadersAllowList) { + return false + } + for i := range this.InsertHeadersAllowList { + if this.InsertHeadersAllowList[i] != that1.InsertHeadersAllowList[i] { + 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 len(m.InsertHeadersAllowList) > 0 { + for iNdEx := len(m.InsertHeadersAllowList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.InsertHeadersAllowList[iNdEx]) + copy(dAtA[i:], m.InsertHeadersAllowList[iNdEx]) + i = encodeVarintParams(dAtA, i, uint64(len(m.InsertHeadersAllowList[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + 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 len(m.InsertHeadersAllowList) > 0 { + for _, s := range m.InsertHeadersAllowList { + l = len(s) + n += 1 + l + sovParams(uint64(l)) + } + } + 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InsertHeadersAllowList", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InsertHeadersAllowList = append(m.InsertHeadersAllowList, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + 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/btclightclient/types/querier.go b/x/btclightclient/types/querier.go index 51537d46c..a35893026 100644 --- a/x/btclightclient/types/querier.go +++ b/x/btclightclient/types/querier.go @@ -20,6 +20,15 @@ func NewQueryContainsRequest(hash string) (*QueryContainsRequest, error) { return res, nil } +func NewQueryHeaderDepthRequest(hash string) (*QueryHeaderDepthRequest, error) { + _, err := types.NewBTCHeaderHashBytesFromHex(hash) + if err != nil { + return nil, err + } + res := &QueryHeaderDepthRequest{Hash: hash} + return res, nil +} + func NewQueryMainChainRequest(req *query.PageRequest) *QueryMainChainRequest { return &QueryMainChainRequest{Pagination: req} } diff --git a/x/btclightclient/types/querier_test.go b/x/btclightclient/types/querier_test.go index e659a1041..5a23c0c12 100644 --- a/x/btclightclient/types/querier_test.go +++ b/x/btclightclient/types/querier_test.go @@ -12,10 +12,10 @@ import ( ) func TestNewQueryHashesRequest(t *testing.T) { - headerBytes := bbn.GetBaseBTCHeaderBytes() - headerHashBytes := headerBytes.Hash() + baseHeader := types.SimnetGenesisBlock() + req := query.PageRequest{ - Key: headerHashBytes.MustMarshal(), + Key: baseHeader.Hash.MustMarshal(), } newQueryHashes := types.NewQueryHashesRequest(&req) if newQueryHashes == nil { @@ -55,9 +55,10 @@ func FuzzNewQueryContainsRequest(f *testing.F) { } func TestNewQueryMainChainRequest(t *testing.T) { - headerBytes := bbn.GetBaseBTCHeaderBytes() + baseHeader := types.SimnetGenesisBlock() + req := query.PageRequest{ - Key: headerBytes.MustMarshal(), + Key: baseHeader.Header.MustMarshal(), } newQueryMainChain := types.NewQueryMainChainRequest(&req) if newQueryMainChain == nil { diff --git a/x/btclightclient/types/query.pb.go b/x/btclightclient/types/query.pb.go index 123bb92d6..65d640b56 100644 --- a/x/btclightclient/types/query.pb.go +++ b/x/btclightclient/types/query.pb.go @@ -31,6 +31,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_3961270631e52721, []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_3961270631e52721, []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{} +} + // QueryHashesRequest is request type for the Query/Hashes RPC method. // It involves retrieving all hashes that are maintained by the module. type QueryHashesRequest struct { @@ -41,7 +124,7 @@ func (m *QueryHashesRequest) Reset() { *m = QueryHashesRequest{} } func (m *QueryHashesRequest) String() string { return proto.CompactTextString(m) } func (*QueryHashesRequest) ProtoMessage() {} func (*QueryHashesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{0} + return fileDescriptor_3961270631e52721, []int{2} } func (m *QueryHashesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -87,7 +170,7 @@ func (m *QueryHashesResponse) Reset() { *m = QueryHashesResponse{} } func (m *QueryHashesResponse) String() string { return proto.CompactTextString(m) } func (*QueryHashesResponse) ProtoMessage() {} func (*QueryHashesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{1} + return fileDescriptor_3961270631e52721, []int{3} } func (m *QueryHashesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -133,7 +216,7 @@ func (m *QueryContainsRequest) Reset() { *m = QueryContainsRequest{} } func (m *QueryContainsRequest) String() string { return proto.CompactTextString(m) } func (*QueryContainsRequest) ProtoMessage() {} func (*QueryContainsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{2} + return fileDescriptor_3961270631e52721, []int{4} } func (m *QueryContainsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -171,7 +254,7 @@ func (m *QueryContainsResponse) Reset() { *m = QueryContainsResponse{} } func (m *QueryContainsResponse) String() string { return proto.CompactTextString(m) } func (*QueryContainsResponse) ProtoMessage() {} func (*QueryContainsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{3} + return fileDescriptor_3961270631e52721, []int{5} } func (m *QueryContainsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -217,7 +300,7 @@ func (m *QueryContainsBytesRequest) Reset() { *m = QueryContainsBytesReq func (m *QueryContainsBytesRequest) String() string { return proto.CompactTextString(m) } func (*QueryContainsBytesRequest) ProtoMessage() {} func (*QueryContainsBytesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{4} + return fileDescriptor_3961270631e52721, []int{6} } func (m *QueryContainsBytesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -263,7 +346,7 @@ func (m *QueryContainsBytesResponse) Reset() { *m = QueryContainsBytesRe func (m *QueryContainsBytesResponse) String() string { return proto.CompactTextString(m) } func (*QueryContainsBytesResponse) ProtoMessage() {} func (*QueryContainsBytesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{5} + return fileDescriptor_3961270631e52721, []int{7} } func (m *QueryContainsBytesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -309,7 +392,7 @@ func (m *QueryMainChainRequest) Reset() { *m = QueryMainChainRequest{} } func (m *QueryMainChainRequest) String() string { return proto.CompactTextString(m) } func (*QueryMainChainRequest) ProtoMessage() {} func (*QueryMainChainRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{6} + return fileDescriptor_3961270631e52721, []int{8} } func (m *QueryMainChainRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -355,7 +438,7 @@ func (m *QueryMainChainResponse) Reset() { *m = QueryMainChainResponse{} func (m *QueryMainChainResponse) String() string { return proto.CompactTextString(m) } func (*QueryMainChainResponse) ProtoMessage() {} func (*QueryMainChainResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{7} + return fileDescriptor_3961270631e52721, []int{9} } func (m *QueryMainChainResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -406,7 +489,7 @@ func (m *QueryTipRequest) Reset() { *m = QueryTipRequest{} } func (m *QueryTipRequest) String() string { return proto.CompactTextString(m) } func (*QueryTipRequest) ProtoMessage() {} func (*QueryTipRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{8} + return fileDescriptor_3961270631e52721, []int{10} } func (m *QueryTipRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -444,7 +527,7 @@ func (m *QueryTipResponse) Reset() { *m = QueryTipResponse{} } func (m *QueryTipResponse) String() string { return proto.CompactTextString(m) } func (*QueryTipResponse) ProtoMessage() {} func (*QueryTipResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{9} + return fileDescriptor_3961270631e52721, []int{11} } func (m *QueryTipResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -489,7 +572,7 @@ func (m *QueryBaseHeaderRequest) Reset() { *m = QueryBaseHeaderRequest{} func (m *QueryBaseHeaderRequest) String() string { return proto.CompactTextString(m) } func (*QueryBaseHeaderRequest) ProtoMessage() {} func (*QueryBaseHeaderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{10} + return fileDescriptor_3961270631e52721, []int{12} } func (m *QueryBaseHeaderRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -528,7 +611,7 @@ func (m *QueryBaseHeaderResponse) Reset() { *m = QueryBaseHeaderResponse func (m *QueryBaseHeaderResponse) String() string { return proto.CompactTextString(m) } func (*QueryBaseHeaderResponse) ProtoMessage() {} func (*QueryBaseHeaderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3961270631e52721, []int{11} + return fileDescriptor_3961270631e52721, []int{13} } func (m *QueryBaseHeaderResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -564,7 +647,101 @@ func (m *QueryBaseHeaderResponse) GetHeader() *BTCHeaderInfo { return nil } +// QueryMainChainDepthRequest is the request type for the Query/MainChainDepth RPC +// it contains hex encoded hash of btc block header as parameter +type QueryHeaderDepthRequest struct { + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *QueryHeaderDepthRequest) Reset() { *m = QueryHeaderDepthRequest{} } +func (m *QueryHeaderDepthRequest) String() string { return proto.CompactTextString(m) } +func (*QueryHeaderDepthRequest) ProtoMessage() {} +func (*QueryHeaderDepthRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3961270631e52721, []int{14} +} +func (m *QueryHeaderDepthRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryHeaderDepthRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryHeaderDepthRequest.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 *QueryHeaderDepthRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryHeaderDepthRequest.Merge(m, src) +} +func (m *QueryHeaderDepthRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryHeaderDepthRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryHeaderDepthRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryHeaderDepthRequest proto.InternalMessageInfo + +func (m *QueryHeaderDepthRequest) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +// QueryMainChainDepthResponse is the response type for the Query/MainChainDepth RPC +// it contains depth of the block in main chain +type QueryHeaderDepthResponse struct { + Depth uint64 `protobuf:"varint,1,opt,name=depth,proto3" json:"depth,omitempty"` +} + +func (m *QueryHeaderDepthResponse) Reset() { *m = QueryHeaderDepthResponse{} } +func (m *QueryHeaderDepthResponse) String() string { return proto.CompactTextString(m) } +func (*QueryHeaderDepthResponse) ProtoMessage() {} +func (*QueryHeaderDepthResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3961270631e52721, []int{15} +} +func (m *QueryHeaderDepthResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryHeaderDepthResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryHeaderDepthResponse.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 *QueryHeaderDepthResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryHeaderDepthResponse.Merge(m, src) +} +func (m *QueryHeaderDepthResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryHeaderDepthResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryHeaderDepthResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryHeaderDepthResponse proto.InternalMessageInfo + +func (m *QueryHeaderDepthResponse) GetDepth() uint64 { + if m != nil { + return m.Depth + } + return 0 +} + func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "babylon.btclightclient.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "babylon.btclightclient.v1.QueryParamsResponse") proto.RegisterType((*QueryHashesRequest)(nil), "babylon.btclightclient.v1.QueryHashesRequest") proto.RegisterType((*QueryHashesResponse)(nil), "babylon.btclightclient.v1.QueryHashesResponse") proto.RegisterType((*QueryContainsRequest)(nil), "babylon.btclightclient.v1.QueryContainsRequest") @@ -577,6 +754,8 @@ func init() { proto.RegisterType((*QueryTipResponse)(nil), "babylon.btclightclient.v1.QueryTipResponse") proto.RegisterType((*QueryBaseHeaderRequest)(nil), "babylon.btclightclient.v1.QueryBaseHeaderRequest") proto.RegisterType((*QueryBaseHeaderResponse)(nil), "babylon.btclightclient.v1.QueryBaseHeaderResponse") + proto.RegisterType((*QueryHeaderDepthRequest)(nil), "babylon.btclightclient.v1.QueryHeaderDepthRequest") + proto.RegisterType((*QueryHeaderDepthResponse)(nil), "babylon.btclightclient.v1.QueryHeaderDepthResponse") } func init() { @@ -584,51 +763,59 @@ func init() { } var fileDescriptor_3961270631e52721 = []byte{ - // 694 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xb1, 0x4f, 0x14, 0x4f, - 0x14, 0xc7, 0x19, 0xf8, 0xfd, 0x0e, 0x7c, 0x68, 0xd4, 0x11, 0x15, 0x36, 0x66, 0xc5, 0x45, 0xe0, - 0x04, 0xd9, 0xe1, 0x0e, 0x35, 0x14, 0x16, 0xe6, 0x48, 0x14, 0x0b, 0x13, 0xbc, 0x5c, 0xa5, 0x26, - 0x66, 0xf6, 0x1c, 0x77, 0x37, 0x81, 0x9d, 0x85, 0x19, 0x88, 0xd7, 0x5a, 0x58, 0x1b, 0xed, 0x2c, - 0x2c, 0x4c, 0x6c, 0xad, 0x6c, 0xfc, 0x0f, 0x2c, 0x49, 0x6c, 0x8c, 0x85, 0x31, 0xe0, 0x1f, 0x62, - 0x76, 0xe6, 0x1d, 0xc7, 0x1d, 0x72, 0xb7, 0x44, 0x1a, 0xc2, 0xee, 0xbc, 0xef, 0xfb, 0x7e, 0xe6, - 0x65, 0xbe, 0xb3, 0x07, 0x93, 0x01, 0x0f, 0x1a, 0xab, 0x32, 0x61, 0x81, 0xae, 0xaf, 0xc6, 0x61, - 0x94, 0xfd, 0x15, 0x89, 0x66, 0x5b, 0x25, 0xb6, 0xbe, 0x29, 0x36, 0x1a, 0x7e, 0xba, 0x21, 0xb5, - 0xa4, 0x63, 0x58, 0xe6, 0xb7, 0x97, 0xf9, 0x5b, 0x25, 0x67, 0x24, 0x94, 0xa1, 0x34, 0x55, 0x2c, - 0xfb, 0xcf, 0x0a, 0x9c, 0x4b, 0xa1, 0x94, 0xe1, 0xaa, 0x60, 0x3c, 0x8d, 0x19, 0x4f, 0x12, 0xa9, - 0xb9, 0x8e, 0x65, 0xa2, 0x70, 0x75, 0xa6, 0x2e, 0xd5, 0x9a, 0x54, 0x2c, 0xe0, 0x4a, 0x58, 0x1f, - 0xb6, 0x55, 0x0a, 0x84, 0xe6, 0x25, 0x96, 0xf2, 0x30, 0x4e, 0x4c, 0x31, 0xd6, 0xfa, 0x87, 0x13, - 0x76, 0xc0, 0x98, 0x7a, 0xef, 0x09, 0xd0, 0x87, 0x59, 0xc7, 0x65, 0xae, 0x22, 0xa1, 0xaa, 0x62, - 0x7d, 0x53, 0x28, 0x4d, 0xef, 0x02, 0xb4, 0x3a, 0x8f, 0x92, 0x71, 0x52, 0x1c, 0x2e, 0x4f, 0xf9, - 0x16, 0xc3, 0xcf, 0x30, 0x7c, 0xbb, 0x5d, 0xc4, 0xf0, 0x57, 0x78, 0x28, 0x50, 0x5b, 0xdd, 0xa7, - 0xf4, 0x3e, 0x13, 0x38, 0xd7, 0xd6, 0x5e, 0xa5, 0x32, 0x51, 0x82, 0xd6, 0xa0, 0x10, 0x99, 0x37, - 0xa3, 0x64, 0x7c, 0xa0, 0x78, 0xb2, 0x72, 0xfb, 0xc7, 0xcf, 0xcb, 0x8b, 0x61, 0xac, 0xa3, 0xcd, - 0xc0, 0xaf, 0xcb, 0x35, 0x86, 0x9b, 0xa8, 0x47, 0x3c, 0x4e, 0x9a, 0x0f, 0x4c, 0x37, 0x52, 0xa1, - 0xfc, 0x4a, 0x6d, 0x69, 0x59, 0xf0, 0x67, 0x62, 0x23, 0x6b, 0x59, 0x69, 0x68, 0xa1, 0xaa, 0xd8, - 0x8b, 0xde, 0x6b, 0xa3, 0xee, 0x37, 0xd4, 0xd3, 0x3d, 0xa9, 0x2d, 0x52, 0x1b, 0x76, 0x04, 0x23, - 0x86, 0x7a, 0x49, 0x26, 0x9a, 0xc7, 0xc9, 0xde, 0x58, 0x56, 0xe0, 0xbf, 0xcc, 0xca, 0x0c, 0xe4, - 0x5f, 0xa1, 0x4d, 0x27, 0x6f, 0x01, 0xce, 0x77, 0x38, 0xe1, 0x84, 0x1c, 0x18, 0xaa, 0xe3, 0x3b, - 0x63, 0x37, 0x54, 0xdd, 0x7b, 0xf6, 0x18, 0x8c, 0xb5, 0x89, 0x6c, 0x43, 0x64, 0xa4, 0xfb, 0x19, - 0xd1, 0x65, 0x11, 0x9c, 0xbf, 0x09, 0x72, 0x58, 0x3d, 0x45, 0xbe, 0x07, 0x3c, 0x4e, 0x96, 0xb2, - 0x8d, 0x1d, 0xf7, 0x09, 0xf9, 0x48, 0xe0, 0x42, 0xa7, 0x03, 0x72, 0x55, 0x60, 0x30, 0x32, 0x43, - 0xb3, 0xa7, 0x64, 0xb8, 0x5c, 0xf4, 0x0f, 0xcd, 0x55, 0x6b, 0xc2, 0xf7, 0x93, 0xe7, 0xb2, 0xda, - 0x14, 0x1e, 0xdf, 0x91, 0x38, 0x0b, 0xa7, 0x0d, 0x66, 0x2d, 0x4e, 0x71, 0x1b, 0x5e, 0x0d, 0xce, - 0xb4, 0x5e, 0x21, 0xf3, 0x1d, 0x28, 0x58, 0x6b, 0x1c, 0x49, 0x7e, 0x64, 0xd4, 0x79, 0xa3, 0x38, - 0x8f, 0x0a, 0x57, 0xc2, 0x2e, 0x37, 0xfd, 0x1e, 0xc3, 0xc5, 0x03, 0x2b, 0xc7, 0x65, 0x5b, 0xfe, - 0x32, 0x08, 0xff, 0x9b, 0xee, 0xf4, 0x0d, 0x81, 0x82, 0x8d, 0x2b, 0x9d, 0xeb, 0xd2, 0xe6, 0xe0, - 0xad, 0xe1, 0xf8, 0x79, 0xcb, 0x2d, 0xb5, 0x77, 0xed, 0xe5, 0xb7, 0xdf, 0x6f, 0xfb, 0x27, 0xe8, - 0x15, 0x76, 0xf8, 0xa5, 0x85, 0xd1, 0x7e, 0x47, 0x60, 0xa8, 0x79, 0x7a, 0x29, 0xeb, 0xe5, 0xd3, - 0x91, 0x5b, 0x67, 0x3e, 0xbf, 0x00, 0xd1, 0x66, 0x0d, 0xda, 0x24, 0x9d, 0xe8, 0x82, 0xd6, 0x0c, - 0x09, 0xfd, 0x44, 0xe0, 0x54, 0x5b, 0xb4, 0xe8, 0x8d, 0xbc, 0x86, 0xfb, 0xa3, 0xeb, 0xdc, 0x3c, - 0xa2, 0x0a, 0x59, 0xe7, 0x0d, 0xeb, 0x0c, 0x2d, 0xe6, 0x60, 0xb5, 0x78, 0xef, 0x09, 0x9c, 0xd8, - 0xcb, 0x1b, 0xed, 0x39, 0x9d, 0xce, 0xf0, 0x3b, 0xa5, 0x23, 0x28, 0x10, 0xf2, 0xba, 0x81, 0x9c, - 0xa2, 0x57, 0xbb, 0x40, 0xae, 0xf1, 0xd8, 0xde, 0x9e, 0xf4, 0x15, 0x81, 0x81, 0x5a, 0x9c, 0xd2, - 0x99, 0x5e, 0x46, 0xad, 0x38, 0x3a, 0xb3, 0xb9, 0x6a, 0x11, 0x67, 0xca, 0xe0, 0x8c, 0x53, 0xb7, - 0x0b, 0x8e, 0x8e, 0x53, 0xfa, 0x81, 0x00, 0xb4, 0xf2, 0x46, 0x7b, 0x6e, 0xfc, 0x40, 0x6a, 0x9d, - 0xf2, 0x51, 0x24, 0x48, 0x37, 0x67, 0xe8, 0xa6, 0xe9, 0x64, 0x17, 0xba, 0xec, 0xf2, 0xb2, 0xd9, - 0xad, 0xac, 0x7c, 0xdd, 0x71, 0xc9, 0xf6, 0x8e, 0x4b, 0x7e, 0xed, 0xb8, 0xe4, 0xf5, 0xae, 0xdb, - 0xb7, 0xbd, 0xeb, 0xf6, 0x7d, 0xdf, 0x75, 0xfb, 0x1e, 0xdd, 0xea, 0xf5, 0x79, 0x7a, 0xd1, 0xd9, - 0xd9, 0x7c, 0xaf, 0x82, 0x82, 0xf9, 0x71, 0xb0, 0xf0, 0x27, 0x00, 0x00, 0xff, 0xff, 0x88, 0x96, - 0xa9, 0x76, 0xf0, 0x08, 0x00, 0x00, + // 823 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcf, 0x4f, 0x13, 0x4d, + 0x18, 0xc7, 0xbb, 0xfc, 0xe8, 0x0b, 0xc3, 0xfb, 0xe6, 0xd5, 0xb1, 0x6a, 0xd9, 0x98, 0x02, 0x8b, + 0x94, 0x02, 0xb2, 0x43, 0x8b, 0x1a, 0x0e, 0x26, 0x9a, 0x62, 0x14, 0x0f, 0x26, 0xb5, 0x69, 0x3c, + 0xa8, 0x89, 0x99, 0x96, 0x71, 0x77, 0x13, 0xba, 0xb3, 0x74, 0x17, 0x62, 0x63, 0xbc, 0x78, 0xf0, + 0x6c, 0xf4, 0xe6, 0xc1, 0x83, 0x89, 0xf1, 0xe6, 0xc9, 0x3f, 0x82, 0x23, 0x89, 0x17, 0xe3, 0x81, + 0x18, 0xf0, 0x8f, 0xf0, 0x68, 0x76, 0xe6, 0xd9, 0xb6, 0xdb, 0x42, 0x77, 0x1b, 0xb9, 0x10, 0x76, + 0xe6, 0xf9, 0x3e, 0xdf, 0xcf, 0x3c, 0xcc, 0x7e, 0x59, 0x34, 0x57, 0xa5, 0xd5, 0xe6, 0x16, 0xb7, + 0x49, 0xd5, 0xab, 0x6d, 0x59, 0x86, 0xe9, 0xff, 0x64, 0xb6, 0x47, 0x76, 0xf3, 0x64, 0x7b, 0x87, + 0x35, 0x9a, 0xba, 0xd3, 0xe0, 0x1e, 0xc7, 0x93, 0x50, 0xa6, 0x87, 0xcb, 0xf4, 0xdd, 0xbc, 0x9a, + 0x32, 0xb8, 0xc1, 0x45, 0x15, 0xf1, 0x7f, 0x93, 0x02, 0xf5, 0x92, 0xc1, 0xb9, 0xb1, 0xc5, 0x08, + 0x75, 0x2c, 0x42, 0x6d, 0x9b, 0x7b, 0xd4, 0xb3, 0xb8, 0xed, 0xc2, 0xee, 0x62, 0x8d, 0xbb, 0x75, + 0xee, 0x92, 0x2a, 0x75, 0x99, 0xf4, 0x21, 0xbb, 0xf9, 0x2a, 0xf3, 0x68, 0x9e, 0x38, 0xd4, 0xb0, + 0x6c, 0x51, 0x0c, 0xb5, 0xfa, 0xc9, 0x84, 0x5d, 0x30, 0xb2, 0x3e, 0x7b, 0x72, 0xbd, 0x43, 0x1b, + 0xb4, 0x0e, 0x0c, 0x5a, 0x0a, 0xe1, 0x07, 0xbe, 0x73, 0x49, 0x2c, 0x96, 0xd9, 0xf6, 0x0e, 0x73, + 0x3d, 0xed, 0x21, 0x3a, 0x17, 0x5a, 0x75, 0x1d, 0x6e, 0xbb, 0x0c, 0xdf, 0x44, 0x49, 0x29, 0x4e, + 0x2b, 0xd3, 0x4a, 0x6e, 0xa2, 0x30, 0xa3, 0x9f, 0x38, 0x10, 0x5d, 0x4a, 0x8b, 0x23, 0x7b, 0x07, + 0x53, 0x89, 0x32, 0xc8, 0xb4, 0x27, 0xe0, 0xb6, 0x41, 0x5d, 0x93, 0x05, 0x6e, 0xf8, 0x0e, 0x42, + 0xed, 0xf3, 0x42, 0xeb, 0xac, 0x2e, 0x87, 0xa3, 0xfb, 0xc3, 0xd1, 0xe5, 0x1f, 0x01, 0x86, 0xa3, + 0x97, 0xa8, 0xc1, 0x40, 0x5b, 0xee, 0x50, 0x6a, 0x5f, 0x15, 0xc0, 0x0e, 0xda, 0x03, 0x76, 0x05, + 0x25, 0x4d, 0xb1, 0x92, 0x56, 0xa6, 0x87, 0x73, 0xff, 0x16, 0x6f, 0xfc, 0x38, 0x98, 0x5a, 0x33, + 0x2c, 0xcf, 0xdc, 0xa9, 0xea, 0x35, 0x5e, 0x27, 0x70, 0x88, 0x9a, 0x49, 0x2d, 0x3b, 0x78, 0x20, + 0x5e, 0xd3, 0x61, 0xae, 0x5e, 0xac, 0xac, 0x6f, 0x30, 0xba, 0xc9, 0x1a, 0x7e, 0xcb, 0x62, 0xd3, + 0x63, 0x6e, 0x19, 0x7a, 0xe1, 0xbb, 0x21, 0xea, 0x21, 0x41, 0x3d, 0x1f, 0x49, 0x2d, 0x91, 0x42, + 0xd8, 0x26, 0x4a, 0x09, 0xea, 0x75, 0x6e, 0x7b, 0xd4, 0xb2, 0x5b, 0x63, 0x29, 0xa1, 0x11, 0xdf, + 0x4a, 0x0c, 0xe4, 0x6f, 0xa1, 0x45, 0x27, 0x6d, 0x15, 0x9d, 0xef, 0x72, 0x82, 0x09, 0xa9, 0x68, + 0xac, 0x06, 0x6b, 0xc2, 0x6e, 0xac, 0xdc, 0x7a, 0xd6, 0x08, 0x9a, 0x0c, 0x89, 0x64, 0x43, 0x60, + 0xc4, 0x9d, 0x8c, 0xe0, 0xb2, 0x86, 0xd4, 0xe3, 0x04, 0x31, 0xac, 0x9e, 0x02, 0xdf, 0x7d, 0x6a, + 0xd9, 0xeb, 0xfe, 0xc1, 0x4e, 0xfb, 0x86, 0x7c, 0x52, 0xd0, 0x85, 0x6e, 0x07, 0xe0, 0x2a, 0xa2, + 0x7f, 0x4c, 0x31, 0x34, 0x79, 0x4b, 0x26, 0x0a, 0xb9, 0x3e, 0x97, 0xbb, 0x35, 0xe1, 0x7b, 0xf6, + 0x33, 0x5e, 0x0e, 0x84, 0xa7, 0x77, 0x25, 0xce, 0xa2, 0xff, 0x05, 0x66, 0xc5, 0x72, 0x82, 0x57, + 0xb2, 0x82, 0xce, 0xb4, 0x97, 0x80, 0xf9, 0x16, 0x4a, 0x4a, 0x6b, 0x18, 0x49, 0x7c, 0x64, 0xd0, + 0x69, 0x69, 0x98, 0x47, 0x91, 0xba, 0x4c, 0x6e, 0x07, 0x7e, 0x8f, 0xd1, 0xc5, 0x9e, 0x9d, 0x53, + 0xb3, 0x5d, 0x86, 0xe6, 0x72, 0xeb, 0x36, 0x73, 0x3c, 0xf3, 0xb8, 0x1b, 0x35, 0x0e, 0x37, 0x6a, + 0x05, 0xa5, 0x7b, 0xcb, 0x01, 0x26, 0x85, 0x46, 0x37, 0xfd, 0x05, 0x21, 0x18, 0x29, 0xcb, 0x87, + 0xc2, 0xef, 0x71, 0x34, 0x2a, 0x24, 0xf8, 0xad, 0x82, 0x92, 0x32, 0x8b, 0xf0, 0x72, 0x1f, 0xce, + 0xde, 0x10, 0x54, 0xf5, 0xb8, 0xe5, 0x92, 0x44, 0x5b, 0x78, 0xf5, 0xed, 0xd7, 0xbb, 0xa1, 0x59, + 0x3c, 0x43, 0xa2, 0xb2, 0x57, 0x40, 0xc9, 0x90, 0x8a, 0x86, 0x0a, 0x65, 0x65, 0x34, 0x54, 0x38, + 0xfb, 0x62, 0x41, 0x41, 0xa0, 0xbd, 0x57, 0xd0, 0x58, 0xf0, 0xce, 0x62, 0x12, 0xe5, 0xd3, 0x95, + 0x56, 0xea, 0x4a, 0x7c, 0x01, 0xa0, 0x2d, 0x09, 0xb4, 0x39, 0x3c, 0xdb, 0x07, 0x2d, 0x88, 0x06, + 0xfc, 0x45, 0x41, 0xff, 0x85, 0x02, 0x05, 0x5f, 0x8d, 0x6b, 0xd8, 0x19, 0x58, 0xea, 0xb5, 0x01, + 0x55, 0xc0, 0xba, 0x22, 0x58, 0x17, 0x71, 0x2e, 0x06, 0xab, 0xc4, 0xfb, 0xa0, 0xa0, 0xf1, 0x56, + 0xca, 0xe0, 0xc8, 0xe9, 0x74, 0x47, 0x9e, 0x9a, 0x1f, 0x40, 0x01, 0x90, 0x57, 0x04, 0x64, 0x16, + 0x5f, 0xee, 0x03, 0x59, 0xa7, 0x96, 0xfc, 0x9f, 0x81, 0x5f, 0x2b, 0x68, 0xb8, 0x62, 0x39, 0x78, + 0x31, 0xca, 0xa8, 0x1d, 0x42, 0xea, 0x52, 0xac, 0x5a, 0xc0, 0xc9, 0x0a, 0x9c, 0x69, 0x9c, 0xe9, + 0x83, 0xe3, 0x59, 0x0e, 0xfe, 0xa8, 0x20, 0xd4, 0x4e, 0x19, 0x1c, 0x79, 0xf0, 0x9e, 0xac, 0x52, + 0x0b, 0x83, 0x48, 0x80, 0x6e, 0x59, 0xd0, 0xcd, 0xe3, 0xb9, 0x3e, 0x74, 0x7e, 0x64, 0xcb, 0xc4, + 0xc2, 0x9f, 0x15, 0x34, 0xd1, 0x11, 0x3f, 0x38, 0xd2, 0xb2, 0x37, 0xda, 0xd4, 0xd5, 0x81, 0x34, + 0xc0, 0x49, 0x04, 0xe7, 0x02, 0x9e, 0xef, 0xc3, 0x29, 0x32, 0x8f, 0xbc, 0xf0, 0xdf, 0xe3, 0x97, + 0xc5, 0xd2, 0xde, 0x61, 0x46, 0xd9, 0x3f, 0xcc, 0x28, 0x3f, 0x0f, 0x33, 0xca, 0x9b, 0xa3, 0x4c, + 0x62, 0xff, 0x28, 0x93, 0xf8, 0x7e, 0x94, 0x49, 0x3c, 0xba, 0x1e, 0xf5, 0xf9, 0xf0, 0xbc, 0xbb, + 0xb7, 0xf8, 0x9e, 0xa8, 0x26, 0xc5, 0xa7, 0xe2, 0xea, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x25, + 0x1e, 0x8e, 0x7d, 0x26, 0x0b, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -643,6 +830,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) // Hashes retrieves the hashes maintained by the module. Hashes(ctx context.Context, in *QueryHashesRequest, opts ...grpc.CallOption) (*QueryHashesResponse, error) // Contains checks whether a hash is maintained by the module. @@ -659,6 +848,9 @@ type QueryClient interface { // BaseHeader returns the base BTC header of the chain. This header is defined // on genesis. BaseHeader(ctx context.Context, in *QueryBaseHeaderRequest, opts ...grpc.CallOption) (*QueryBaseHeaderResponse, error) + // HeaderDepth returns the depth of the header in main chain or error if the + // block is not found or it exists on fork + HeaderDepth(ctx context.Context, in *QueryHeaderDepthRequest, opts ...grpc.CallOption) (*QueryHeaderDepthResponse, error) } type queryClient struct { @@ -669,6 +861,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.btclightclient.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) Hashes(ctx context.Context, in *QueryHashesRequest, opts ...grpc.CallOption) (*QueryHashesResponse, error) { out := new(QueryHashesResponse) err := c.cc.Invoke(ctx, "/babylon.btclightclient.v1.Query/Hashes", in, out, opts...) @@ -723,8 +924,19 @@ func (c *queryClient) BaseHeader(ctx context.Context, in *QueryBaseHeaderRequest return out, nil } +func (c *queryClient) HeaderDepth(ctx context.Context, in *QueryHeaderDepthRequest, opts ...grpc.CallOption) (*QueryHeaderDepthResponse, error) { + out := new(QueryHeaderDepthResponse) + err := c.cc.Invoke(ctx, "/babylon.btclightclient.v1.Query/HeaderDepth", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { + // Params queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) // Hashes retrieves the hashes maintained by the module. Hashes(context.Context, *QueryHashesRequest) (*QueryHashesResponse, error) // Contains checks whether a hash is maintained by the module. @@ -741,12 +953,18 @@ type QueryServer interface { // BaseHeader returns the base BTC header of the chain. This header is defined // on genesis. BaseHeader(context.Context, *QueryBaseHeaderRequest) (*QueryBaseHeaderResponse, error) + // HeaderDepth returns the depth of the header in main chain or error if the + // block is not found or it exists on fork + HeaderDepth(context.Context, *QueryHeaderDepthRequest) (*QueryHeaderDepthResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. 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) Hashes(ctx context.Context, req *QueryHashesRequest) (*QueryHashesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Hashes not implemented") } @@ -765,11 +983,32 @@ func (*UnimplementedQueryServer) Tip(ctx context.Context, req *QueryTipRequest) func (*UnimplementedQueryServer) BaseHeader(ctx context.Context, req *QueryBaseHeaderRequest) (*QueryBaseHeaderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method BaseHeader not implemented") } +func (*UnimplementedQueryServer) HeaderDepth(ctx context.Context, req *QueryHeaderDepthRequest) (*QueryHeaderDepthResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HeaderDepth not implemented") +} 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.btclightclient.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_Hashes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryHashesRequest) if err := dec(in); err != nil { @@ -878,10 +1117,32 @@ func _Query_BaseHeader_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Query_HeaderDepth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryHeaderDepthRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).HeaderDepth(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btclightclient.v1.Query/HeaderDepth", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).HeaderDepth(ctx, req.(*QueryHeaderDepthRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "babylon.btclightclient.v1.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, { MethodName: "Hashes", Handler: _Query_Hashes_Handler, @@ -906,11 +1167,71 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "BaseHeader", Handler: _Query_BaseHeader_Handler, }, + { + MethodName: "HeaderDepth", + Handler: _Query_HeaderDepth_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "babylon/btclightclient/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 *QueryHashesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1326,37 +1647,115 @@ func (m *QueryBaseHeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { - offset -= sovQuery(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *QueryHeaderDepthRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *QueryHashesRequest) Size() (n int) { - if m == nil { - return 0 - } + +func (m *QueryHeaderDepthRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryHeaderDepthRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Pagination != nil { - l = m.Pagination.Size() - n += 1 + l + sovQuery(uint64(l)) + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *QueryHashesResponse) Size() (n int) { - if m == nil { - return 0 +func (m *QueryHeaderDepthResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - var l int - _ = l - if len(m.Hashes) > 0 { + return dAtA[:n], nil +} + +func (m *QueryHeaderDepthResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryHeaderDepthResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Depth != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Depth)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + 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 *QueryHashesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryHashesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Hashes) > 0 { for _, e := range m.Hashes { l = e.Size() n += 1 + l + sovQuery(uint64(l)) @@ -1495,12 +1894,170 @@ func (m *QueryBaseHeaderResponse) Size() (n int) { return n } +func (m *QueryHeaderDepthRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryHeaderDepthResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Depth != 0 { + n += 1 + sovQuery(uint64(m.Depth)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } 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 *QueryHashesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2495,6 +3052,157 @@ func (m *QueryBaseHeaderResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryHeaderDepthRequest) 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: QueryHeaderDepthRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryHeaderDepthRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + 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 *QueryHeaderDepthResponse) 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: QueryHeaderDepthResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryHeaderDepthResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Depth", wireType) + } + m.Depth = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Depth |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/btclightclient/types/query.pb.gw.go b/x/btclightclient/types/query.pb.gw.go index 14fc314c2..026611cb2 100644 --- a/x/btclightclient/types/query.pb.gw.go +++ b/x/btclightclient/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 + +} + var ( filter_Query_Hashes_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -213,12 +231,89 @@ func local_request_Query_BaseHeader_0(ctx context.Context, marshaler runtime.Mar } +func request_Query_HeaderDepth_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryHeaderDepthRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := client.HeaderDepth(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_HeaderDepth_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryHeaderDepthRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := server.HeaderDepth(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // 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_Hashes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -357,6 +452,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_HeaderDepth_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_HeaderDepth_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_HeaderDepth_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -398,6 +516,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_Hashes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -518,10 +656,32 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_HeaderDepth_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_HeaderDepth_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_HeaderDepth_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btclightclient", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Hashes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btclightclient", "v1", "hashes"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_Contains_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btclightclient", "v1", "contains"}, "", runtime.AssumeColonVerbOpt(false))) @@ -533,9 +693,13 @@ var ( pattern_Query_Tip_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btclightclient", "v1", "tip"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_BaseHeader_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btclightclient", "v1", "baseheader"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_HeaderDepth_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylon", "btclightclient", "v1", "depth", "hash"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + forward_Query_Hashes_0 = runtime.ForwardResponseMessage forward_Query_Contains_0 = runtime.ForwardResponseMessage @@ -547,4 +711,6 @@ var ( forward_Query_Tip_0 = runtime.ForwardResponseMessage forward_Query_BaseHeader_0 = runtime.ForwardResponseMessage + + forward_Query_HeaderDepth_0 = runtime.ForwardResponseMessage ) diff --git a/x/btclightclient/types/tx.pb.go b/x/btclightclient/types/tx.pb.go index 4790b7e81..3f22f09f1 100644 --- a/x/btclightclient/types/tx.pb.go +++ b/x/btclightclient/types/tx.pb.go @@ -7,6 +7,8 @@ import ( context "context" fmt "fmt" github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "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" @@ -29,24 +31,24 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// MsgInsertHeader defines the message for incoming header bytes -type MsgInsertHeader struct { - Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` - Header *github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,2,opt,name=header,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"header,omitempty"` +// MsgInsertHeaders defines the message for multiple incoming header bytes +type MsgInsertHeaders struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + Headers []github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,2,rep,name=headers,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"headers,omitempty"` } -func (m *MsgInsertHeader) Reset() { *m = MsgInsertHeader{} } -func (m *MsgInsertHeader) String() string { return proto.CompactTextString(m) } -func (*MsgInsertHeader) ProtoMessage() {} -func (*MsgInsertHeader) Descriptor() ([]byte, []int) { +func (m *MsgInsertHeaders) Reset() { *m = MsgInsertHeaders{} } +func (m *MsgInsertHeaders) String() string { return proto.CompactTextString(m) } +func (*MsgInsertHeaders) ProtoMessage() {} +func (*MsgInsertHeaders) Descriptor() ([]byte, []int) { return fileDescriptor_5f638eee60234021, []int{0} } -func (m *MsgInsertHeader) XXX_Unmarshal(b []byte) error { +func (m *MsgInsertHeaders) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgInsertHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgInsertHeaders) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgInsertHeader.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgInsertHeaders.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -56,41 +58,41 @@ func (m *MsgInsertHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, err return b[:n], nil } } -func (m *MsgInsertHeader) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgInsertHeader.Merge(m, src) +func (m *MsgInsertHeaders) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgInsertHeaders.Merge(m, src) } -func (m *MsgInsertHeader) XXX_Size() int { +func (m *MsgInsertHeaders) XXX_Size() int { return m.Size() } -func (m *MsgInsertHeader) XXX_DiscardUnknown() { - xxx_messageInfo_MsgInsertHeader.DiscardUnknown(m) +func (m *MsgInsertHeaders) XXX_DiscardUnknown() { + xxx_messageInfo_MsgInsertHeaders.DiscardUnknown(m) } -var xxx_messageInfo_MsgInsertHeader proto.InternalMessageInfo +var xxx_messageInfo_MsgInsertHeaders proto.InternalMessageInfo -func (m *MsgInsertHeader) GetSigner() string { +func (m *MsgInsertHeaders) GetSigner() string { if m != nil { return m.Signer } return "" } -// MsgInsertHeaderResponse defines the response for the InsertHeader transaction -type MsgInsertHeaderResponse struct { +// MsgInsertHeadersResponse defines the response for the InsertHeaders transaction +type MsgInsertHeadersResponse struct { } -func (m *MsgInsertHeaderResponse) Reset() { *m = MsgInsertHeaderResponse{} } -func (m *MsgInsertHeaderResponse) String() string { return proto.CompactTextString(m) } -func (*MsgInsertHeaderResponse) ProtoMessage() {} -func (*MsgInsertHeaderResponse) Descriptor() ([]byte, []int) { +func (m *MsgInsertHeadersResponse) Reset() { *m = MsgInsertHeadersResponse{} } +func (m *MsgInsertHeadersResponse) String() string { return proto.CompactTextString(m) } +func (*MsgInsertHeadersResponse) ProtoMessage() {} +func (*MsgInsertHeadersResponse) Descriptor() ([]byte, []int) { return fileDescriptor_5f638eee60234021, []int{1} } -func (m *MsgInsertHeaderResponse) XXX_Unmarshal(b []byte) error { +func (m *MsgInsertHeadersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgInsertHeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgInsertHeadersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgInsertHeaderResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgInsertHeadersResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -100,21 +102,120 @@ func (m *MsgInsertHeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]b return b[:n], nil } } -func (m *MsgInsertHeaderResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgInsertHeaderResponse.Merge(m, src) +func (m *MsgInsertHeadersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgInsertHeadersResponse.Merge(m, src) } -func (m *MsgInsertHeaderResponse) XXX_Size() int { +func (m *MsgInsertHeadersResponse) XXX_Size() int { return m.Size() } -func (m *MsgInsertHeaderResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgInsertHeaderResponse.DiscardUnknown(m) +func (m *MsgInsertHeadersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgInsertHeadersResponse.DiscardUnknown(m) } -var xxx_messageInfo_MsgInsertHeaderResponse proto.InternalMessageInfo +var xxx_messageInfo_MsgInsertHeadersResponse proto.InternalMessageInfo + +// MsgUpdateParams defines a message for updating btc light client 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 btc light client 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_5f638eee60234021, []int{2} +} +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_5f638eee60234021, []int{3} +} +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((*MsgInsertHeader)(nil), "babylon.btclightclient.v1.MsgInsertHeader") - proto.RegisterType((*MsgInsertHeaderResponse)(nil), "babylon.btclightclient.v1.MsgInsertHeaderResponse") + proto.RegisterType((*MsgInsertHeaders)(nil), "babylon.btclightclient.v1.MsgInsertHeaders") + proto.RegisterType((*MsgInsertHeadersResponse)(nil), "babylon.btclightclient.v1.MsgInsertHeadersResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "babylon.btclightclient.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.btclightclient.v1.MsgUpdateParamsResponse") } func init() { @@ -122,24 +223,35 @@ func init() { } var fileDescriptor_5f638eee60234021 = []byte{ - // 265 bytes of a gzipped FileDescriptorProto + // 443 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0x65, 0x86, 0xfa, 0x25, 0x15, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x35, 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xaa, 0xf4, 0x41, - 0x2c, 0x88, 0x06, 0xa5, 0x6a, 0x2e, 0x7e, 0xdf, 0xe2, 0x74, 0xcf, 0xbc, 0xe2, 0xd4, 0xa2, 0x12, - 0x8f, 0xd4, 0xc4, 0x94, 0xd4, 0x22, 0x21, 0x31, 0x2e, 0xb6, 0xe2, 0xcc, 0xf4, 0xbc, 0xd4, 0x22, - 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x28, 0x4f, 0x28, 0x80, 0x8b, 0x2d, 0x03, 0xac, 0x42, - 0x82, 0x49, 0x81, 0x51, 0x83, 0xc7, 0xc9, 0xe2, 0xd6, 0x3d, 0x79, 0x93, 0xf4, 0xcc, 0x92, 0x8c, - 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xd5, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, - 0x7e, 0x49, 0x65, 0x41, 0x6a, 0xb1, 0x9e, 0x53, 0x88, 0x33, 0xc4, 0x70, 0xa7, 0xca, 0x92, 0xd4, - 0xe2, 0x20, 0xa8, 0x39, 0x4a, 0x92, 0x5c, 0xe2, 0x68, 0x96, 0x07, 0xa5, 0x16, 0x17, 0xe4, 0xe7, - 0x15, 0xa7, 0x1a, 0x95, 0x73, 0x31, 0xfb, 0x16, 0xa7, 0x0b, 0x15, 0x70, 0xf1, 0xa0, 0xb8, 0x4d, - 0x4b, 0x0f, 0xa7, 0x07, 0xf5, 0xd0, 0x8c, 0x92, 0x32, 0x22, 0x5e, 0x2d, 0xcc, 0x5a, 0x25, 0x06, - 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, - 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x23, 0xe4, 0xd7, - 0x0a, 0xf4, 0x98, 0x01, 0x7b, 0x3e, 0x89, 0x0d, 0x1c, 0xd2, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x26, 0x30, 0x43, 0x4d, 0xc0, 0x01, 0x00, 0x00, + 0x2c, 0x88, 0x06, 0x29, 0xf1, 0xe4, 0xfc, 0xe2, 0xdc, 0xfc, 0x62, 0xfd, 0xdc, 0xe2, 0x74, 0x90, + 0x41, 0xb9, 0xc5, 0xe9, 0x50, 0x09, 0x35, 0xdc, 0xb6, 0x15, 0x24, 0x16, 0x25, 0xe6, 0x16, 0x43, + 0xd5, 0x49, 0x42, 0x0c, 0x88, 0x87, 0x98, 0x0c, 0xe1, 0x40, 0xa4, 0x94, 0xba, 0x19, 0xb9, 0x04, + 0x7c, 0x8b, 0xd3, 0x3d, 0xf3, 0x8a, 0x53, 0x8b, 0x4a, 0x3c, 0x52, 0x13, 0x53, 0x52, 0x8b, 0x8a, + 0x85, 0xc4, 0xb8, 0xd8, 0x8a, 0x33, 0xd3, 0xf3, 0x52, 0x8b, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, + 0x83, 0xa0, 0x3c, 0xa1, 0x20, 0x2e, 0xf6, 0x0c, 0x88, 0x12, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x1e, + 0x27, 0x8b, 0x5b, 0xf7, 0xe4, 0x4d, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, + 0xf5, 0xa1, 0xee, 0x49, 0xce, 0x48, 0xcc, 0xcc, 0x83, 0x71, 0xf4, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, + 0xf5, 0x9c, 0x42, 0x9c, 0x21, 0xc6, 0x3b, 0x55, 0x96, 0xa4, 0x16, 0x07, 0xc1, 0x0c, 0xb2, 0xe2, + 0x6e, 0x7a, 0xbe, 0x41, 0x0b, 0x6a, 0x81, 0x92, 0x14, 0x97, 0x04, 0xba, 0x63, 0x82, 0x52, 0x8b, + 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x95, 0x66, 0x31, 0x72, 0xf1, 0xfb, 0x16, 0xa7, 0x87, 0x16, 0xa4, + 0x24, 0x96, 0xa4, 0x06, 0x80, 0xbd, 0x27, 0x64, 0xc6, 0xc5, 0x99, 0x58, 0x5a, 0x92, 0x91, 0x5f, + 0x94, 0x59, 0x52, 0x09, 0x71, 0xab, 0x93, 0xc4, 0xa5, 0x2d, 0xba, 0x22, 0x50, 0x2f, 0x3a, 0xa6, + 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x07, 0x97, 0x14, 0x65, 0xe6, 0xa5, 0x07, 0x21, 0x94, 0x0a, 0xd9, + 0x73, 0xb1, 0x41, 0x02, 0x48, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x51, 0x0f, 0x67, 0x9c, + 0xe8, 0x41, 0xac, 0x72, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xcd, 0x8a, 0x0f, 0xe4, + 0x6a, 0x84, 0x81, 0x4a, 0x92, 0x5c, 0xe2, 0x68, 0x6e, 0x83, 0xb9, 0xdb, 0xe8, 0x23, 0x23, 0x17, + 0xb3, 0x6f, 0x71, 0xba, 0x50, 0x31, 0x17, 0x2f, 0x6a, 0x28, 0x6b, 0xe3, 0xb1, 0x14, 0x3d, 0x14, + 0xa4, 0x8c, 0x49, 0x50, 0x0c, 0x0f, 0x32, 0x06, 0xa1, 0x3c, 0x2e, 0x1e, 0x94, 0x00, 0xd3, 0xc2, + 0x6f, 0x0c, 0xb2, 0x5a, 0x29, 0x23, 0xe2, 0xd5, 0xc2, 0x6c, 0x94, 0x62, 0x6d, 0x78, 0xbe, 0x41, + 0x8b, 0xd1, 0x29, 0xe0, 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, 0xcc, 0x08, + 0xa5, 0x96, 0x0a, 0xf4, 0xc4, 0x0c, 0x4e, 0x3e, 0x49, 0x6c, 0xe0, 0xe4, 0x6a, 0x0c, 0x08, 0x00, + 0x00, 0xff, 0xff, 0x64, 0x60, 0x2a, 0x32, 0x61, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -154,9 +266,10 @@ 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 { - // InsertHeader adds a header to the BTC light client chain maintained by - // Babylon. - InsertHeader(ctx context.Context, in *MsgInsertHeader, opts ...grpc.CallOption) (*MsgInsertHeaderResponse, error) + // InsertHeaders adds a batch of headers to the BTC light client chain + InsertHeaders(ctx context.Context, in *MsgInsertHeaders, opts ...grpc.CallOption) (*MsgInsertHeadersResponse, error) + // UpdateParams defines a method for updating btc light client module parameters. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) } type msgClient struct { @@ -167,9 +280,18 @@ func NewMsgClient(cc grpc1.ClientConn) MsgClient { return &msgClient{cc} } -func (c *msgClient) InsertHeader(ctx context.Context, in *MsgInsertHeader, opts ...grpc.CallOption) (*MsgInsertHeaderResponse, error) { - out := new(MsgInsertHeaderResponse) - err := c.cc.Invoke(ctx, "/babylon.btclightclient.v1.Msg/InsertHeader", in, out, opts...) +func (c *msgClient) InsertHeaders(ctx context.Context, in *MsgInsertHeaders, opts ...grpc.CallOption) (*MsgInsertHeadersResponse, error) { + out := new(MsgInsertHeadersResponse) + err := c.cc.Invoke(ctx, "/babylon.btclightclient.v1.Msg/InsertHeaders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.btclightclient.v1.Msg/UpdateParams", in, out, opts...) if err != nil { return nil, err } @@ -178,37 +300,59 @@ func (c *msgClient) InsertHeader(ctx context.Context, in *MsgInsertHeader, opts // MsgServer is the server API for Msg service. type MsgServer interface { - // InsertHeader adds a header to the BTC light client chain maintained by - // Babylon. - InsertHeader(context.Context, *MsgInsertHeader) (*MsgInsertHeaderResponse, error) + // InsertHeaders adds a batch of headers to the BTC light client chain + InsertHeaders(context.Context, *MsgInsertHeaders) (*MsgInsertHeadersResponse, error) + // UpdateParams defines a method for updating btc light client module parameters. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. type UnimplementedMsgServer struct { } -func (*UnimplementedMsgServer) InsertHeader(ctx context.Context, req *MsgInsertHeader) (*MsgInsertHeaderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method InsertHeader not implemented") +func (*UnimplementedMsgServer) InsertHeaders(ctx context.Context, req *MsgInsertHeaders) (*MsgInsertHeadersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InsertHeaders not implemented") +} +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_InsertHeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgInsertHeader) +func _Msg_InsertHeaders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgInsertHeaders) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).InsertHeaders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btclightclient.v1.Msg/InsertHeaders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).InsertHeaders(ctx, req.(*MsgInsertHeaders)) + } + return interceptor(ctx, in, info, handler) +} + +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).InsertHeader(ctx, in) + return srv.(MsgServer).UpdateParams(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/babylon.btclightclient.v1.Msg/InsertHeader", + FullMethod: "/babylon.btclightclient.v1.Msg/UpdateParams", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).InsertHeader(ctx, req.(*MsgInsertHeader)) + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) } return interceptor(ctx, in, info, handler) } @@ -218,15 +362,19 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ HandlerType: (*MsgServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "InsertHeader", - Handler: _Msg_InsertHeader_Handler, + MethodName: "InsertHeaders", + Handler: _Msg_InsertHeaders_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "babylon/btclightclient/v1/tx.proto", } -func (m *MsgInsertHeader) Marshal() (dAtA []byte, err error) { +func (m *MsgInsertHeaders) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -236,27 +384,29 @@ func (m *MsgInsertHeader) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgInsertHeader) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgInsertHeaders) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgInsertHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgInsertHeaders) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.Header != nil { - { - size := m.Header.Size() - i -= size - if _, err := m.Header.MarshalTo(dAtA[i:]); err != nil { - return 0, err + if len(m.Headers) > 0 { + for iNdEx := len(m.Headers) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.Headers[iNdEx].Size() + i -= size + if _, err := m.Headers[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) } - i = encodeVarintTx(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x12 } if len(m.Signer) > 0 { i -= len(m.Signer) @@ -268,7 +418,7 @@ func (m *MsgInsertHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *MsgInsertHeaderResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgInsertHeadersResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -278,12 +428,75 @@ func (m *MsgInsertHeaderResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgInsertHeaderResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgInsertHeadersResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgInsertHeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgInsertHeadersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +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 @@ -302,7 +515,7 @@ func encodeVarintTx(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *MsgInsertHeader) Size() (n int) { +func (m *MsgInsertHeaders) Size() (n int) { if m == nil { return 0 } @@ -312,14 +525,40 @@ func (m *MsgInsertHeader) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.Header != nil { - l = m.Header.Size() + if len(m.Headers) > 0 { + for _, e := range m.Headers { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgInsertHeadersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +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 *MsgInsertHeaderResponse) Size() (n int) { +func (m *MsgUpdateParamsResponse) Size() (n int) { if m == nil { return 0 } @@ -334,7 +573,7 @@ func sovTx(x uint64) (n int) { func sozTx(x uint64) (n int) { return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *MsgInsertHeader) Unmarshal(dAtA []byte) error { +func (m *MsgInsertHeaders) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -357,10 +596,10 @@ func (m *MsgInsertHeader) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgInsertHeader: wiretype end group for non-group") + return fmt.Errorf("proto: MsgInsertHeaders: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgInsertHeader: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgInsertHeaders: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -397,7 +636,7 @@ func (m *MsgInsertHeader) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Headers", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -425,8 +664,173 @@ func (m *MsgInsertHeader) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } var v github_com_babylonchain_babylon_types.BTCHeaderBytes - m.Header = &v - if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Headers = append(m.Headers, v) + if err := m.Headers[len(m.Headers)-1].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 *MsgInsertHeadersResponse) 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: MsgInsertHeadersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgInsertHeadersResponse: 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 (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 @@ -451,7 +855,7 @@ func (m *MsgInsertHeader) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgInsertHeaderResponse) Unmarshal(dAtA []byte) error { +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -474,10 +878,10 @@ func (m *MsgInsertHeaderResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgInsertHeaderResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgInsertHeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: diff --git a/x/btclightclient/types/utils.go b/x/btclightclient/types/utils.go new file mode 100644 index 000000000..e5181ea4a --- /dev/null +++ b/x/btclightclient/types/utils.go @@ -0,0 +1,18 @@ +package types + +import ( + "time" + + "github.com/btcsuite/btcd/chaincfg" +) + +func BlocksPerRetarget(params *chaincfg.Params) int32 { + targetTimespan := int64(params.TargetTimespan / time.Second) + targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) + return int32(targetTimespan / targetTimePerBlock) +} + +func IsRetargetBlock(info *BTCHeaderInfo, params *chaincfg.Params) bool { + hI32 := int32(info.Height) + return hI32%BlocksPerRetarget(params) == 0 +} diff --git a/x/btclightclient/types/work.go b/x/btclightclient/types/work.go index 45a114cbc..b5ef4148a 100644 --- a/x/btclightclient/types/work.go +++ b/x/btclightclient/types/work.go @@ -4,10 +4,15 @@ import ( sdkmath "cosmossdk.io/math" bbn "github.com/babylonchain/babylon/types" "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/wire" ) func CalcWork(header *bbn.BTCHeaderBytes) sdkmath.Uint { - return sdkmath.NewUintFromBigInt(blockchain.CalcWork(header.Bits())) + return CalcHeaderWork(header.ToBlockHeader()) +} + +func CalcHeaderWork(header *wire.BlockHeader) sdkmath.Uint { + return sdkmath.NewUintFromBigInt(blockchain.CalcWork(header.Bits)) } func CumulativeWork(childWork sdkmath.Uint, parentWork sdkmath.Uint) sdkmath.Uint { diff --git a/x/btclightclient/types/work_test.go b/x/btclightclient/types/work_test.go index e4e20a458..a68410244 100644 --- a/x/btclightclient/types/work_test.go +++ b/x/btclightclient/types/work_test.go @@ -1,9 +1,9 @@ package types_test import ( + sdkmath "cosmossdk.io/math" "github.com/babylonchain/babylon/testutil/datagen" "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" "math/rand" "testing" ) @@ -14,12 +14,12 @@ func FuzzCumulativeWork(f *testing.F) { r := rand.New(rand.NewSource(seed)) numa := r.Uint64() numb := r.Uint64() - biga := sdk.NewUint(numa) - bigb := sdk.NewUint(numb) + biga := sdkmath.NewUint(numa) + bigb := sdkmath.NewUint(numb) gotSum := types.CumulativeWork(biga, bigb) - expectedSum := sdk.NewUint(0) + expectedSum := sdkmath.NewUint(0) expectedSum = expectedSum.Add(biga) expectedSum = expectedSum.Add(bigb) diff --git a/x/btcstaking/README.md b/x/btcstaking/README.md new file mode 100644 index 000000000..473a7eee5 --- /dev/null +++ b/x/btcstaking/README.md @@ -0,0 +1,658 @@ +# BTCStaking + +The BTC staking module is responsible for maintaining the set of finality +providers and BTC delegations under them. This includes: + +- handling requests for creating finality providers, +- handling requests for creating BTC delegations, +- handlilng requests for submitting signatures of covenant emulators, +- handling requests for unbonding BTC delegations, and +- proactively refreshing the active set of finality providers and BTC + delegations. + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Concepts](#concepts) +- [States](#states) + - [Parameters](#parameters) + - [Finality providers](#finality-providers) + - [BTC delegations](#btc-delegations) + - [BTC delegation index](#btc-delegation-index) + - [Voting power table](#voting-power-table) + - [Params](#params) +- [Messages](#messages) + - [MsgCreateFinalityProvider](#msgcreatefinalityprovider) + - [MsgCreateBTCDelegation](#msgcreatebtcdelegation) + - [MsgAddCovenantSigs](#msgaddcovenantsigs) + - [MsgBTCUndelegate](#msgbtcundelegate) + - [MsgUpdateParams](#msgupdateparams) +- [BeginBlocker](#beginblocker) +- [Events](#events) +- [Queries](#queries) + +## Concepts + +Babylon's Bitcoin Staking protocol allows bitcoin holders to *trustlessly* stake +their bitcoins for providing economic security to the Babylon chain and other +Proof-of-Stake (PoS) blockchains, *without bridging their bitcoins elsewhere*. +The protocol consists of the following participants: + +- **BTC staker (aka delegator)** who delegates their bitcoins to a finality + provider in order to obtain staking reward. +- **Finality provider** who receives bitcoin delegations and participates in the + *finality vote round* on top of the CometBFT consensus. +- **Covenant emulation committee** who serves as the + [covenants](https://covenants.info) to enforce spending conditions on bitcoins + staked on Babylon. + +The BTC Staking module is a major component in Babylon's BTC Staking protocol. +At a high level, the participants interact with the BTC Staking module as +follows: + +1. A finality provider registers itself on the BTC Staking module. +2. A BTC staker delegates some bitcoins to the finality provider. This involves + the following steps: + 1. The BTC staker submits a *staking transaction* to Bitcoin. The staking + transaction locks its bitcoins for a long period of time and specifies + slashing conditions. + 2. The BTC staker constructs the following transactions (whose specifications + can be found [here](../../docs/staking-script.md)): + - a *slashing transaction* that can spend the staking transaction once the + finality provider is slashed, + - an *unbonding transaction* that spends the staking transaction to start + the early unbonding process, and + - an *unbonding slashing transaction* that can spend the unbonding + transaction once the finality provider is slashed. The BTC staker + pre-signs the slashing transaction and unbonding slashing transaction. + 3. Once the staking transaction is confirmed on Bitcoin, the BTC staker sends + the staking transaction, its inclusion proof, slashing transaction, + unbonding transaction, and unbonding slashing transaction to Babylon. +3. The covenant committee verifies spending conditions of the staking + transaction, and submits its signatures on the BTC staker's transactions. At + this point, the finality provider receives bitcoins and thus voting power + from the BTC delegation. +4. Upon each new block, the BTC Staking module will record the voting power + table of finality providers. + +Babylon's [Finality module](../finality) will make use of the voting power table +maintained in the BTC Staking module to determine the finalization status of +each block, identify equivocations of finality providers, and slash BTC +delegations under culpable finality providers. + +A BTC staker can unbond early by signing the unbonding transaction and +submitting it to Bitcoin. The BTC Staking module identifies unbonding requests +through this signature reported by the [BTC staking tracker +daemon](https://github.com/babylonchain/vigilante), and will consider the BTC +delegation unbonded immediately upon such a signature. + +## States + +The BTC Staking module maintains the following KV stores. + +### Parameters + +The [parameter storage](./keeper/params.go) maintains the BTC Staking module's +parameters. The BTC Staking module's parameters are represented as a `Params` +[object](../../proto/babylon/btcstaking/v1/params.proto) defined as follows: + +```protobuf +// Params defines the parameters for the module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // covenant_pks is the list of public keys held by the covenant committee + // each PK follows encoding in BIP-340 spec on Bitcoin + repeated bytes covenant_pks = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // covenant_quorum is the minimum number of signatures needed for the covenant + // multisignature + uint32 covenant_quorum = 2; + // slashing address is the address that the slashed BTC goes to + // the address is in string on Bitcoin + string slashing_address = 3; + // min_slashing_tx_fee_sat is the minimum amount of tx fee (quantified + // in Satoshi) needed for the pre-signed slashing tx + int64 min_slashing_tx_fee_sat = 4; + // min_commission_rate is the chain-wide minimum commission rate that a finality provider can charge their delegators + string min_commission_rate = 5 [ + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // slashing_rate determines the portion of the staked amount to be slashed, + // expressed as a decimal (e.g., 0.5 for 50%). + string slashing_rate = 6 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + uint32 max_active_finality_providers = 7; + // min_unbonding_time is the minimum time for unbonding transaction timelock in BTC blocks + uint32 min_unbonding_time = 8; +} +``` + +### Finality providers + +The [finality provider storage](./keeper/finality_providers.go) maintains all +finality providers. The key is the finality provider's Bitcoin Secp256k1 public +key in [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) +format, and the value is a `FinalityProvider` +[object](../../proto/babylon/btcstaking/v1/btcstaking.proto) representing a +finality provider. + +```protobuf +// FinalityProvider defines a finality provider +message FinalityProvider { + // description defines the description terms for the finality provider. + cosmos.staking.v1beta1.Description description = 1; + // commission defines the commission rate of the finality provider. + string commission = 2 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; + // babylon_pk is the Babylon secp256k1 PK of this finality provider + cosmos.crypto.secp256k1.PubKey babylon_pk = 3; + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 5; + // slashed_babylon_height indicates the Babylon height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_babylon_height = 6; + // slashed_btc_height indicates the BTC height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + uint64 slashed_btc_height = 7; +} +``` + +### BTC delegations + +The [BTC delegation storage](./keeper/btc_delegations.go) maintains all BTC +delegations. The key is the staking transaction hash corresponding to the BTC +delegation, and the value is a `BTCDelegation` object. The `BTCDelegation` +[structure](../../proto/babylon/btcstaking/v1/btcstaking.proto) includes +information of a BTC delegation and a structure `BTCUndelegation` that includes +information of its early unbonding path. The staking transaction's hash uniquely +identifies a `BTCDelegation` as creating a BTC delegation requires the staker to +submit a staking transaction to Bitcoin. + +```protobuf + +// BTCDelegation defines a BTC delegation +message BTCDelegation { + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + cosmos.crypto.secp256k1.PubKey babylon_pk = 1; + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 3; + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + repeated bytes fp_btc_pk_list = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // start_height is the start BTC height of the BTC delegation + // it is the start BTC height of the timelock + uint64 start_height = 5; + // end_height is the end height of the BTC delegation + // it is the end BTC height of the timelock - w + uint64 end_height = 6; + // total_sat is the total amount of BTC stakes in this delegation + // quantified in satoshi + uint64 total_sat = 7; + // staking_tx is the staking tx + bytes staking_tx = 8; + // staking_output_idx is the index of the staking output in the staking tx + uint32 staking_output_idx = 9; + // slashing_tx is the slashing tx + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + bytes slashing_tx = 10 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + bytes delegator_sig = 11 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // covenant_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_sigs = 12; + // unbonding_time describes how long the funds will be locked either in unbonding output + // or slashing change output + uint32 unbonding_time = 13; + // btc_undelegation is the information about the early unbonding path of the BTC delegation + BTCUndelegation btc_undelegation = 14; +} + +// BTCUndelegation contains the information about the early unbonding path of the BTC delegation +message BTCUndelegation { + // unbonding_tx is the transaction which will transfer the funds from staking + // output to unbonding output. Unbonding output will usually have lower timelock + // than staking output. + bytes unbonding_tx = 1; + // slashing_tx is the slashing tx for unbonding transactions + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + bytes slashing_tx = 2 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_unbonding_sig is the signature on the unbonding tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It effectively proves that the delegator wants to unbond and thus + // Babylon will consider this BTC delegation unbonded. Delegator's BTC + // on Bitcoin will be unbonded after timelock + bytes delegator_unbonding_sig = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // delegator_slashing_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the unbonding tx output. + bytes delegator_slashing_sig = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // covenant_slashing_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + repeated CovenantAdaptorSignatures covenant_slashing_sigs = 5; + // covenant_unbonding_sig_list is the list of signatures on the unbonding tx + // by covenant members + // It must be provided after processing undelagate message by Babylon + repeated SignatureInfo covenant_unbonding_sig_list = 6; +} +``` + +### BTC delegation index + +The [BTC delegation index storage](./keeper/btc_delegators.go) maintains an +index between the BTC delegator and its BTC delegations. The key is the BTC +delegator's Bitcoin secp256k1 public key in BIP-340 format, and the value is a +`BTCDelegatorDelegationIndex` +[object](../../proto/babylon/btcstaking/v1/btcstaking.proto) that contains +staking transaction hashes of the delegator's BTC delegations. + +```protobuf +// BTCDelegatorDelegationIndex is a list of staking tx hashes of BTC delegations from the same delegator. +message BTCDelegatorDelegationIndex { + repeated bytes staking_tx_hash_list = 1; +} +``` + +### Voting power table + +The [voting power table storage](./keeper/voting_power_table.go) maintains the +voting power table of all finality providers at each height of the Babylon +chain. The key is the block height concatenated with the finality provider's +Bitcoin secp256k1 public key in BIP-340 format, and the value is the finality +provider's voting power quantified in Satoshis. + +### Params + +The [parameter storage](./keeper/params.go) maintains the parameters for the BTC +staking module. + +```protobuf +// Params defines the parameters for the module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // covenant_pks is the list of public keys held by the covenant committee + // each PK follows encoding in BIP-340 spec on Bitcoin + repeated bytes covenant_pks = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // covenant_quorum is the minimum number of signatures needed for the covenant + // multisignature + uint32 covenant_quorum = 2; + // slashing address is the address that the slashed BTC goes to + // the address is in string on Bitcoin + string slashing_address = 3; + // min_slashing_tx_fee_sat is the minimum amount of tx fee (quantified + // in Satoshi) needed for the pre-signed slashing tx + int64 min_slashing_tx_fee_sat = 4; + // min_commission_rate is the chain-wide minimum commission rate that a finality provider can charge their delegators + string min_commission_rate = 5 [ + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // slashing_rate determines the portion of the staked amount to be slashed, + // expressed as a decimal (e.g., 0.5 for 50%). + string slashing_rate = 6 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + uint32 max_active_finality_providers = 7; + // min_unbonding_time is the minimum time for unbonding transaction timelock in BTC blocks + uint32 min_unbonding_time = 8; +} +``` + +## Messages + +The BTC Staking module handles the following messages from finality providers, +BTC stakers (aka delegators), and covenant emulators. The message formats are +defined at +[proto/babylon/btcstaking/v1/tx.proto](../../proto/babylon/btcstaking/v1/tx.proto). +The message handlers are defined at +[x/btcstaking/keeper/msg_server.go](./keeper/msg_server.go). + +### MsgCreateFinalityProvider + +The `MsgCreateFinalityProvider` message is used for creating a finality +provider. It is typically submitted by a finality provider via the [finality +provider](https://github.com/babylonchain/finality-provider) program. + +```protobuf +message MsgCreateFinalityProvider { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + + // description defines the description terms for the finality provider. + cosmos.staking.v1beta1.Description description = 2; + // commission defines the commission rate of finality provider. + string commission = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; + // babylon_pk is the Babylon secp256k1 PK of this finality provider + cosmos.crypto.secp256k1.PubKey babylon_pk = 4; + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 6; +} +``` + +Upon `MsgCreateFinalityProvider`, a Babylon node will execute as follows: + +1. Verify a [proof of + possession](https://rist.tech.cornell.edu/papers/pkreg.pdf) indicating the + ownership of both the Babylon and Bitcoin secret keys. +2. Ensure the given commission rate is at least the `MinCommissionRate` in the + parameters and at most 100%. +3. Ensure the finality provider does not exist already. +4. Create a `FinalityProvider` object and save it to finality provider storage. + +### MsgCreateBTCDelegation + +The `MsgCreateBTCDelegation` message is used for delegating some bitcoins to a +finality provider. It is typically submitted by a BTC delegator via the [BTC +staker](https://github.com/babylonchain/btc-staker) program. + +```protobuf +// MsgCreateBTCDelegation is the message for creating a BTC delegation +message MsgCreateBTCDelegation { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + cosmos.crypto.secp256k1.PubKey babylon_pk = 2; + // pop is the proof of possession of babylon_pk and btc_pk + ProofOfPossession pop = 3; + // btc_pk is the Bitcoin secp256k1 PK of the BTC delegator + bytes btc_pk = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // fp_btc_pk_list is the list of Bitcoin secp256k1 PKs of the finality providers, if there is more than one + // finality provider pk it means that delegation is re-staked + repeated bytes fp_btc_pk_list = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_time is the time lock used in staking transaction + uint32 staking_time = 6; + // staking_value is the amount of satoshis locked in staking output + int64 staking_value = 7; + // staking_tx is the staking tx along with the merkle proof of inclusion in btc block + babylon.btccheckpoint.v1.TransactionInfo staking_tx = 8; + // slashing_tx is the slashing tx + // Note that the tx itself does not contain signatures, which are off-chain. + bytes slashing_tx = 9 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + // The staking tx output further needs signatures from covenant and finality provider in + // order to be spendable. + bytes delegator_slashing_sig = 10 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // unbonding_time is the time lock used when funds are being unbonded. It is be used in: + // - unbonding transaction, time lock spending path + // - staking slashing transaction, change output + // - unbonding slashing transaction, change output + // It must be smaller than math.MaxUInt16 and larger that max(MinUnbondingTime, CheckpointFinalizationTimeout) + uint32 unbonding_time = 11; + // fields related to unbonding transaction + // unbonding_tx is a bitcoin unbonding transaction i.e transaction that spends + // staking output and sends it to the unbonding output + bytes unbonding_tx = 12; + // unbonding_value is amount of satoshis locked in unbonding output. + // NOTE: staking_value and unbonding_value could be different because of the difference between the fee for staking tx and that for unbonding + int64 unbonding_value = 13; + // unbonding_slashing_tx is the slashing tx which slash unbonding contract + // Note that the tx itself does not contain signatures, which are off-chain. + bytes unbonding_slashing_tx = 14 [ (gogoproto.customtype) = "BTCSlashingTx" ]; + // delegator_unbonding_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + bytes delegator_unbonding_slashing_sig = 15 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +``` + +Upon `MsgCreateBTCDelegation`, a Babylon node will execute as follows: + +1. Ensure the given unbonding time is larger than `max(MinUnbondingTime, + CheckpointFinalizationTimeout)`, where `MinUnbondingTime` and + `CheckpointFinalizationTimeout` are module parameters from BTC Staking module + and BTC Checkpoint module, respectively. +2. Verify a [proof of + possession](https://rist.tech.cornell.edu/papers/pkreg.pdf) indicating the + ownership of both the Babylon and Bitcoin secret keys. +3. Ensure the finality providers that the bitcoins are delegated to are known to + Babylon. +4. Verify the staking transaction and slashing transaction, including + 1. Ensure the staking transaction is not duplicated with an existing BTC + delegation known to Babylon. + 2. Ensure the information provided in the request is consistent with the + staking transaction's BTC script. + 3. Ensure the staking transaction is `BTCConfirmationDepth`-deep in Bitcoin, + where `BTCConfirmationDepth` is a module parameter specified in the BTC + Checkpoint module. + 4. Ensure the staking transaction's timelock has more than + `CheckpointFinalizationTimeout` BTC blocks left. + 5. Verify the Merkle proof of inclusion of the staking transaction against + the BTC light client. + 6. Ensure the staking transaction and slashing transaction are valid and + consistent, as per the [specification](../../docs/staking-script.md) of + their formats. + 7. Verify the Schnorr signature on the slashing transaction signed by the BTC + delegator. +5. Verify the unbonding transaction and unbonding slashing transaction, + including + 1. Ensure the unbonding transaction's input points to the staking + transaction. + 2. Verify the Schnorr signature on the slashing path of the unbonding + transaction by the BTC delegator. + 3. Verify the unbonding transaction and the unbonding path's slashing + transaction are valid and consistent, as per the + [specification](../../docs/staking-script.md) of their formats. +6. Create a `BTCDelegation` object and save it to the BTC delegation storage and + the BTC delegation index storage. + +### MsgAddCovenantSigs + +The `MsgAddCovenantSigs` message is used for submitting signatures on a BTC +delegation signed by a covenant committee member. It is typically submitted by a +covenant committee member via the [covenant +emulator](https://github.com/babylonchain/covenant-emulator) program. + +```protobuf +// MsgAddCovenantSigs is the message for handling signatures from a covenant member +message MsgAddCovenantSigs { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // pk is the BTC public key of the covenant member + bytes pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 3; + // sigs is a list of adaptor signatures of the covenant + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + repeated bytes slashing_tx_sigs = 4; + // unbonding_tx_sig is the signature of the covenant on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + bytes unbonding_tx_sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; + // slashing_unbonding_tx_sigs is a list of adaptor signatures of the covenant + // on slashing tx corresponding to unbonding tx submitted to babylon + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + repeated bytes slashing_unbonding_tx_sigs = 6; +} +``` + +Upon `AddCovenantSigs`, a Babylon node will execute as follows: + +1. Ensure the given BTC delegation is known to Babylon. +2. Ensure the given covenant public key is in the covenant committee. +3. Verify each covenant adaptor signature on the slashing transaction. Note that + each covenant adaptor signature is encrypted by a finality provider's BTC + public key. +4. Verify the covenant Schnorr signature on the unbonding transactions. +5. Verify each covenant adaptor signature on the slashing transaction of the + unbonding path. +6. Add the covenant signatures to the given `BTCDelegation` in the BTC + delegation storage. + +### MsgBTCUndelegate + +The `MsgBTCUndelegate` message is used for unbonding bitcoins from a given +finality provider. It is typically reported by the [BTC staking +tracker](https://github.com/babylonchain/vigilante-private/tree/dev/btcstaking-tracker) +program which proactively monitors unbonding transactions on Bitcoin. + +```protobuf +// MsgBTCUndelegate is the message for handling signature on unbonding tx +// from its delegator. This signature effectively proves that the delegator +// wants to unbond this BTC delegation +message MsgBTCUndelegate { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 2; + // unbonding_tx_sig is the signature of the staker on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + bytes unbonding_tx_sig = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +``` + +Upon `BTCUndelegate`, a Babylon node will execute as follows: + +1. Ensure the given BTC delegation is still active. +2. Verify the Schnorr signature on the unbonding transaction from the BTC + delegator. If valid, this signature effectively proves that the BTC delegator + wants to unbond this BTC delegation from Babylon. +3. Add the Schnorr signature to the `BTCDelegation` in the BTC delegation + storage. Babylon will consider this BTC delegation to be unbonded from now + on. + +### MsgUpdateParams + +The `MsgUpdateParams` message is used for updating the module parameters for the +BTC Staking module. It can only be executed via a govenance proposal. + +```protobuf +// MsgUpdateParams defines a message for updating btcstaking 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 finality parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} +``` + +## BeginBlocker + +Upon `BeginBlock`, the BTC Staking module will execute the following: + +1. Index the current BTC tip height. This will be used for determining the + status of BTC delegations. +2. Record the voting power table at the current height, by iterating all BTC + delegations. +3. If the BTC Staking protocol is activated, i.e., there exists at least 1 + active BTC delegation, then record the reward distribution w.r.t. the active + finality providers and active BTC delegations. + +The logic is defined at [x/btcstaking/abci.go]((./abci.go)). + +## Events + +The BTC staking module emits a set of events as follows. The events are defined +at `proto/babylon/btcstaking/v1/events.proto`. + +```protobuf +// EventNewFinalityProvider is the event emitted when a finality provider is created +message EventNewFinalityProvider { FinalityProvider fp = 1; } + +// EventNewBTCDelegation is the event emitted when a BTC delegation is created +// NOTE: the BTC delegation is not active thus does not have voting power yet +// only after it receives a covenant signature it becomes activated and has voting power +message EventNewBTCDelegation { BTCDelegation btc_del = 1; } + +// EventActivateBTCDelegation is the event emitted when covenant activates a BTC delegation +// such that the BTC delegation starts to have voting power in its timelock period +message EventActivateBTCDelegation { BTCDelegation btc_del = 1; } + +// EventUnbondingBTCDelegation is the event emitted when an unbonding BTC delegation +// receives all signatures needed for becoming unbonded +message EventUnbondedBTCDelegation { + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + bytes btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + repeated bytes fp_btc_pk_list = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // staking_tx_hash is the hash of the staking tx. + // (fp_pks..., del_pk, staking_tx_hash) uniquely identifies a BTC delegation + string staking_tx_hash = 3; + // unbonding_tx_hash is the hash of the unbonding tx. + string unbonding_tx_hash = 4; + // from_state is the last state the BTC delegation was at + BTCDelegationStatus from_state = 5; +} + +// EventSelectiveSlashing is the event emitted when an adversarial +// finality provider selectively slashes a BTC delegation. This will +// result in slashing of all BTC delegations under this finality provider. +message EventSelectiveSlashing { + // evidence is the evidence of selective slashing + SelectiveSlashingEvidence evidence = 1; +} +// SelectiveSlashingEvidence is the evidence that the finality provider +// selectively slashed a BTC delegation +// NOTE: it's possible that a slashed finality provider exploits the +// SelectiveSlashingEvidence endpoint while it is actually slashed due to +// equivocation. But such behaviour does not affect the system's security +// or gives any benefit for the adversary +message SelectiveSlashingEvidence { + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + string staking_tx_hash = 1; + // fp_btc_pk is the BTC PK of the finality provider who + // launches the selective slashing offence + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // recovered_fp_btc_sk is the finality provider's BTC SK recovered from + // the covenant adaptor/Schnorr signature pair. It is the consequence + // of selective slashing. + bytes recovered_fp_btc_sk = 3; +} +``` + +## Queries + +The BTC staking module provides a set of queries about the status of finality +providers and BTC delegations, listed at +[docs.babylonchain.io](https://docs.babylonchain.io/docs/developer-guides/grpcrestapi#tag/BTCStaking). + diff --git a/x/btcstaking/abci.go b/x/btcstaking/abci.go new file mode 100644 index 000000000..b81a4cc7c --- /dev/null +++ b/x/btcstaking/abci.go @@ -0,0 +1,23 @@ +package btcstaking + +import ( + "context" + "time" + + "github.com/babylonchain/babylon/x/btcstaking/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" +) + +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + return k.BeginBlocker(ctx) +} + +func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + + return []abci.ValidatorUpdate{}, nil +} diff --git a/x/btcstaking/client/cli/query.go b/x/btcstaking/client/cli/query.go new file mode 100644 index 000000000..9d70db427 --- /dev/null +++ b/x/btcstaking/client/cli/query.go @@ -0,0 +1,229 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string) *cobra.Command { + // Group btcstaking queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand(CmdQueryParams()) + cmd.AddCommand(CmdFinalityProviders()) + cmd.AddCommand(CmdBTCDelegations()) + cmd.AddCommand(CmdFinalityProvidersAtHeight()) + cmd.AddCommand(CmdFinalityProviderPowerAtHeight()) + cmd.AddCommand(CmdActivatedHeight()) + cmd.AddCommand(CmdFinalityProviderDelegations()) + + return cmd +} + +func CmdFinalityProviders() *cobra.Command { + cmd := &cobra.Command{ + Use: "finality-providers", + Short: "retrieve all finality providers", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.FinalityProviders(cmd.Context(), &types.QueryFinalityProvidersRequest{ + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "finality-providers") + + return cmd +} + +func CmdBTCDelegations() *cobra.Command { + cmd := &cobra.Command{ + Use: "btc-delegations [status]", + Short: "retrieve all BTC delegations under the given status (pending, active, unbonding, unbonded, any)", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + status, err := types.NewBTCDelegationStatusFromString(args[0]) + if err != nil { + return err + } + + res, err := queryClient.BTCDelegations(cmd.Context(), &types.QueryBTCDelegationsRequest{ + Status: status, + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "btc-delegations") + + return cmd +} + +func CmdFinalityProviderPowerAtHeight() *cobra.Command { + cmd := &cobra.Command{ + Use: "finality-provider-power-at-height [fp_btc_pk_hex] [height]", + Short: "get the voting power of a given finality provider at a given height", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + height, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return err + } + res, err := queryClient.FinalityProviderPowerAtHeight(cmd.Context(), &types.QueryFinalityProviderPowerAtHeightRequest{ + FpBtcPkHex: args[0], + Height: height, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdActivatedHeight() *cobra.Command { + cmd := &cobra.Command{ + Use: "activated-height", + Short: "get activated height, i.e., the first height where there exists 1 finality provider with voting power", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.ActivatedHeight(cmd.Context(), &types.QueryActivatedHeightRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdFinalityProvidersAtHeight() *cobra.Command { + cmd := &cobra.Command{ + Use: "finality-providers-at-height [height]", + Short: "retrieve all finality providers at a given babylon height", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + height, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.ActiveFinalityProvidersAtHeight(cmd.Context(), &types.QueryActiveFinalityProvidersAtHeightRequest{ + Height: height, + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "finality-providers-at-height") + + return cmd +} + +func CmdFinalityProviderDelegations() *cobra.Command { + cmd := &cobra.Command{ + Use: "finality-provider-delegations [fp_pk_hex]", + Short: "retrieve all delegations under a given finality provider", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.FinalityProviderDelegations(cmd.Context(), &types.QueryFinalityProviderDelegationsRequest{ + FpBtcPkHex: args[0], + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "finality-provider-delegations") + + return cmd +} diff --git a/x/btcstaking/client/cli/query_params.go b/x/btcstaking/client/cli/query_params.go new file mode 100644 index 000000000..1672274c1 --- /dev/null +++ b/x/btcstaking/client/cli/query_params.go @@ -0,0 +1,36 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +func CmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "shows the parameters of the module", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/btcstaking/client/cli/tx.go b/x/btcstaking/client/cli/tx.go new file mode 100644 index 000000000..8dc5e51f9 --- /dev/null +++ b/x/btcstaking/client/cli/tx.go @@ -0,0 +1,405 @@ +package cli + +import ( + "encoding/hex" + "fmt" + "strings" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/spf13/cobra" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +const ( + FlagMoniker = "moniker" + FlagIdentity = "identity" + FlagWebsite = "website" + FlagSecurityContact = "security-contact" + FlagDetails = "details" + FlagCommissionRate = "commission-rate" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + NewCreateFinalityProvicerCmd(), + NewCreateBTCDelegationCmd(), + NewAddCovenantSigsCmd(), + NewBTCUndelegateCmd(), + NewSelectiveSlashingEvidenceCmd(), + ) + + return cmd +} + +func NewCreateFinalityProvicerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-finality-provider [babylon_pk] [btc_pk] [pop]", + Args: cobra.ExactArgs(3), + Short: "Create a finality provider", + Long: strings.TrimSpace( + `Create a finality provider.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + fs := cmd.Flags() + + // get description + moniker, _ := fs.GetString(FlagMoniker) + identity, _ := fs.GetString(FlagIdentity) + website, _ := fs.GetString(FlagWebsite) + security, _ := fs.GetString(FlagSecurityContact) + details, _ := fs.GetString(FlagDetails) + description := stakingtypes.NewDescription( + moniker, + identity, + website, + security, + details, + ) + // get commission + rateStr, _ := fs.GetString(FlagCommissionRate) + rate, err := sdkmath.LegacyNewDecFromStr(rateStr) + if err != nil { + return err + } + + // get Babylon PK + babylonPKBytes, err := hex.DecodeString(args[0]) + if err != nil { + return err + } + var babylonPK secp256k1.PubKey + if err := babylonPK.Unmarshal(babylonPKBytes); err != nil { + return err + } + + // get BTC PK + btcPK, err := bbn.NewBIP340PubKeyFromHex(args[1]) + if err != nil { + return err + } + + // get PoP + pop, err := types.NewPoPFromHex(args[2]) + if err != nil { + return err + } + + msg := types.MsgCreateFinalityProvider{ + Signer: clientCtx.FromAddress.String(), + Description: &description, + Commission: &rate, + BabylonPk: &babylonPK, + BtcPk: btcPK, + Pop: pop, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + fs := cmd.Flags() + fs.String(FlagMoniker, "", "The finality provider's (optional) moniker") + fs.String(FlagWebsite, "", "The finality provider's (optional) website") + fs.String(FlagSecurityContact, "", "The finality provider's (optional) security contact email") + fs.String(FlagDetails, "", "The finality provider's (optional) details") + fs.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") + fs.String(FlagCommissionRate, "0", "The initial commission rate percentage") + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func NewCreateBTCDelegationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-btc-delegation [babylon_pk] [btc_pk] [pop] [staking_tx_info] [fp_pk] [staking_time] [staking_value] [slashing_tx] [delegator_slashing_sig] [unbonding_tx] [unbonding_slashing_tx] [unbonding_time] [unbonding_value] [delegator_unbonding_slashing_sig]", + Args: cobra.ExactArgs(14), + Short: "Create a BTC delegation", + Long: strings.TrimSpace( + `Create a BTC delegation.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // get Babylon PK + babylonPKBytes, err := hex.DecodeString(args[0]) + if err != nil { + return err + } + var babylonPK secp256k1.PubKey + if err := babylonPK.Unmarshal(babylonPKBytes); err != nil { + return err + } + + // staker pk + btcPK, err := bbn.NewBIP340PubKeyFromHex(args[1]) + + if err != nil { + return err + } + + // get PoP + pop, err := types.NewPoPFromHex(args[2]) + if err != nil { + return err + } + + // get staking tx info + stakingTxInfo, err := btcctypes.NewTransactionInfoFromHex(args[3]) + if err != nil { + return err + } + + // TODO: Support multiple finality providers + // get finality provider PK + fpPK, err := bbn.NewBIP340PubKeyFromHex(args[4]) + if err != nil { + return err + } + + // get staking time + stakingTime, err := parseLockTime(args[5]) + if err != nil { + return err + } + + stakingValue, err := parseBtcAmount(args[6]) + if err != nil { + return err + } + + // get slashing tx + slashingTx, err := types.NewBTCSlashingTxFromHex(args[7]) + if err != nil { + return err + } + + // get delegator sig on slashing tx + delegatorSlashingSig, err := bbn.NewBIP340SignatureFromHex(args[8]) + if err != nil { + return err + } + + // get unbonding tx + _, unbondingTxBytes, err := bbn.NewBTCTxFromHex(args[9]) + if err != nil { + return err + } + + // get unbonding slashing tx + unbondingSlashingTx, err := types.NewBTCSlashingTxFromHex(args[10]) + if err != nil { + return err + } + + // get staking time + unbondingTime, err := parseLockTime(args[11]) + if err != nil { + return err + } + + unbondingValue, err := parseBtcAmount(args[12]) + if err != nil { + return err + } + + // get delegator sig on unbonding slashing tx + delegatorUnbondingSlashingSig, err := bbn.NewBIP340SignatureFromHex(args[13]) + if err != nil { + return err + } + + msg := types.MsgCreateBTCDelegation{ + Signer: clientCtx.FromAddress.String(), + BabylonPk: &babylonPK, + BtcPk: btcPK, + FpBtcPkList: []bbn.BIP340PubKey{*fpPK}, + Pop: pop, + StakingTime: uint32(stakingTime), + StakingValue: int64(stakingValue), + StakingTx: stakingTxInfo, + SlashingTx: slashingTx, + DelegatorSlashingSig: delegatorSlashingSig, + UnbondingTx: unbondingTxBytes, + UnbondingTime: uint32(unbondingTime), + UnbondingValue: int64(unbondingValue), + UnbondingSlashingTx: unbondingSlashingTx, + DelegatorUnbondingSlashingSig: delegatorUnbondingSlashingSig, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func NewAddCovenantSigsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "add-covenant-sigs [covenant_pk] [staking_tx_hash] [slashing_tx_sig1],[slashing_tx_sig2],... [unbonding_tx_sig] [slashing_unbonding_tx_sig1],[slashing_unbonding_tx_sig2],...", + Args: cobra.ExactArgs(5), + Short: "Add a covenant signature", + Long: strings.TrimSpace( + `Add a covenant signature.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + covPK, err := bbn.NewBIP340PubKeyFromHex(args[0]) + if err != nil { + return fmt.Errorf("invalid public key: %w", err) + } + + // get staking tx hash + stakingTxHash := args[1] + + // parse slashing tx sigs + slashingTxSigs := [][]byte{} + for _, sigHex := range strings.Split(args[2], ",") { + sig, err := asig.NewAdaptorSignatureFromHex(sigHex) + if err != nil { + return fmt.Errorf("invalid covenant signature: %w", err) + } + slashingTxSigs = append(slashingTxSigs, sig.MustMarshal()) + } + + // get covenant signature for unbonding tx + unbondingTxSig, err := bbn.NewBIP340SignatureFromHex(args[3]) + if err != nil { + return err + } + + // parse unbonding slashing tx sigs + unbondingSlashingSigs := [][]byte{} + for _, sigHex := range strings.Split(args[4], ",") { + slashingSig, err := asig.NewAdaptorSignatureFromHex(sigHex) + if err != nil { + return fmt.Errorf("invalid covenant signature: %w", err) + } + unbondingSlashingSigs = append(unbondingSlashingSigs, slashingSig.MustMarshal()) + } + + msg := types.MsgAddCovenantSigs{ + Signer: clientCtx.FromAddress.String(), + Pk: covPK, + StakingTxHash: stakingTxHash, + SlashingTxSigs: slashingTxSigs, + UnbondingTxSig: unbondingTxSig, + SlashingUnbondingTxSigs: unbondingSlashingSigs, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func NewBTCUndelegateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "btc-undelegate [staking_tx_hash] [unbonding_tx_sig]", + Args: cobra.ExactArgs(2), + Short: "Add a signature on the unbonding tx of a BTC delegation identified by a given staking tx hash. ", + Long: strings.TrimSpace( + `Add a signature on the unbonding tx of a BTC delegation identified by a given staking tx hash signed by the delegator. The signature proves that delegator wants to unbond, and Babylon will consider the BTC delegation unbonded.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // get staking tx hash + stakingTxHash := args[0] + + // get delegator signature for unbonding tx + unbondingTxSig, err := bbn.NewBIP340SignatureFromHex(args[1]) + if err != nil { + return err + } + + msg := types.MsgBTCUndelegate{ + Signer: clientCtx.FromAddress.String(), + StakingTxHash: stakingTxHash, + UnbondingTxSig: unbondingTxSig, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func NewSelectiveSlashingEvidenceCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "selective-slashing-evidence [staking_tx_hash] [recovered_fp_btc_sk]", + Args: cobra.ExactArgs(2), + Short: "Add the recovered BTC SK of a finality provider that launched selective slashing offence.", + Long: strings.TrimSpace( + `Add the recovered BTC SK of a finality provider that launched selective slashing offence. The SK is recovered from a pair of Schnorr/adaptor signatures`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // get staking tx hash + stakingTxHash := args[0] + + // get delegator signature for unbonding tx + fpSKBytes, err := hex.DecodeString(args[1]) + if err != nil { + return err + } + + msg := types.MsgSelectiveSlashingEvidence{ + Signer: clientCtx.FromAddress.String(), + StakingTxHash: stakingTxHash, + RecoveredFpBtcSk: fpSKBytes, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/btcstaking/client/cli/utils.go b/x/btcstaking/client/cli/utils.go new file mode 100644 index 000000000..cec621388 --- /dev/null +++ b/x/btcstaking/client/cli/utils.go @@ -0,0 +1,49 @@ +package cli + +import ( + "fmt" + "math" + + sdkmath "cosmossdk.io/math" + "github.com/btcsuite/btcd/btcutil" +) + +func parseLockTime(str string) (uint16, error) { + num, ok := sdkmath.NewIntFromString(str) + + if !ok { + return 0, fmt.Errorf("invalid staking time: %s", str) + } + + if !num.IsUint64() { + return 0, fmt.Errorf("staking time is not valid uint") + } + + asUint64 := num.Uint64() + + if asUint64 > math.MaxUint16 { + return 0, fmt.Errorf("staking time is too large. Max is %d", math.MaxUint16) + } + + return uint16(asUint64), nil +} + +func parseBtcAmount(str string) (btcutil.Amount, error) { + num, ok := sdkmath.NewIntFromString(str) + + if !ok { + return 0, fmt.Errorf("invalid staking value: %s", str) + } + + if num.IsNegative() { + return 0, fmt.Errorf("staking value is negative") + } + + if !num.IsInt64() { + return 0, fmt.Errorf("staking value is not valid uint") + } + + asInt64 := num.Int64() + + return btcutil.Amount(asInt64), nil +} diff --git a/x/btcstaking/genesis.go b/x/btcstaking/genesis.go new file mode 100644 index 000000000..4131d8bd4 --- /dev/null +++ b/x/btcstaking/genesis.go @@ -0,0 +1,22 @@ +package btcstaking + +import ( + "context" + "github.com/babylonchain/babylon/x/btcstaking/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns the module's exported genesis +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Params = k.GetParams(ctx) + + return genesis +} diff --git a/x/btcstaking/genesis_test.go b/x/btcstaking/genesis_test.go new file mode 100644 index 000000000..9b2dd0a14 --- /dev/null +++ b/x/btcstaking/genesis_test.go @@ -0,0 +1,25 @@ +package btcstaking_test + +import ( + "testing" + + keepertest "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/testutil/nullify" + "github.com/babylonchain/babylon/x/btcstaking" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/stretchr/testify/require" +) + +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + } + + k, ctx := keepertest.BTCStakingKeeper(t, nil, nil) + btcstaking.InitGenesis(ctx, *k, genesisState) + got := btcstaking.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + nullify.Fill(&genesisState) + nullify.Fill(got) +} diff --git a/x/btcstaking/keeper/bench_test.go b/x/btcstaking/keeper/bench_test.go new file mode 100644 index 000000000..1379984bd --- /dev/null +++ b/x/btcstaking/keeper/bench_test.go @@ -0,0 +1,98 @@ +package keeper_test + +import ( + "fmt" + "math/rand" + "os" + "runtime/pprof" + "testing" + "time" + + "github.com/babylonchain/babylon/testutil/datagen" + bsmodule "github.com/babylonchain/babylon/x/btcstaking" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/golang/mock/gomock" +) + +func benchBeginBlock(b *testing.B, numFPs int, numDelsUnderFP int) { + r := rand.New(rand.NewSource(time.Now().Unix())) + + // helper + ctrl := gomock.NewController(b) + defer ctrl.Finish() + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(b, btclcKeeper, btccKeeper) + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + h.NoError(err) + + // generate new finality providers + fps := []*types.FinalityProvider{} + for i := 0; i < numFPs; i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + h.NoError(err) + msg := &types.MsgCreateFinalityProvider{ + Signer: datagen.GenRandomAccount().Address, + Description: fp.Description, + Commission: fp.Commission, + BabylonPk: fp.BabylonPk, + BtcPk: fp.BtcPk, + Pop: fp.Pop, + } + _, err = h.MsgServer.CreateFinalityProvider(h.Ctx, msg) + h.NoError(err) + fps = append(fps, fp) + } + + // create new BTC delegations under each finality provider + btcDelMap := map[string][]*types.BTCDelegation{} + for _, fp := range fps { + for i := 0; i < numDelsUnderFP; i++ { + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, _, _, msgCreateBTCDel := h.CreateDelegation( + r, + fp.BtcPk.MustToBTCPK(), + changeAddress.EncodeAddress(), + stakingValue, + 1000, + ) + // retrieve BTC delegation in DB + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + btcDelMap[stakingTxHash] = append(btcDelMap[stakingTxHash], actualDel) + // generate and insert new covenant signatures + // after that, all BTC delegations will have voting power + h.CreateCovenantSigs(r, covenantSKs, msgCreateBTCDel, actualDel) + } + } + + // Start the CPU profiler + cpuProfileFile := fmt.Sprintf("/tmp/btcstaking-beginblock-%d-%d-cpu.pprof", numFPs, numDelsUnderFP) + f, err := os.Create(cpuProfileFile) + if err != nil { + b.Fatal(err) + } + defer f.Close() + if err := pprof.StartCPUProfile(f); err != nil { + b.Fatal(err) + } + defer pprof.StopCPUProfile() + + // Reset timer before the benchmark loop starts + b.ResetTimer() + + for i := 0; i < b.N; i++ { + err = bsmodule.BeginBlocker(h.Ctx, *h.BTCStakingKeeper) + h.NoError(err) + } +} + +func BenchmarkBeginBlock_10_1(b *testing.B) { benchBeginBlock(b, 10, 1) } +func BenchmarkBeginBlock_10_10(b *testing.B) { benchBeginBlock(b, 10, 10) } +func BenchmarkBeginBlock_10_100(b *testing.B) { benchBeginBlock(b, 10, 100) } +func BenchmarkBeginBlock_100_1(b *testing.B) { benchBeginBlock(b, 100, 1) } +func BenchmarkBeginBlock_100_10(b *testing.B) { benchBeginBlock(b, 100, 10) } +func BenchmarkBeginBlock_100_100(b *testing.B) { benchBeginBlock(b, 100, 100) } diff --git a/x/btcstaking/keeper/btc_delegations.go b/x/btcstaking/keeper/btc_delegations.go new file mode 100644 index 000000000..74a67fa98 --- /dev/null +++ b/x/btcstaking/keeper/btc_delegations.go @@ -0,0 +1,36 @@ +package keeper + +import ( + "context" + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/cosmos/cosmos-sdk/runtime" +) + +func (k Keeper) setBTCDelegation(ctx context.Context, btcDel *types.BTCDelegation) { + store := k.btcDelegationStore(ctx) + stakingTxHash := btcDel.MustGetStakingTxHash() + btcDelBytes := k.cdc.MustMarshal(btcDel) + store.Set(stakingTxHash[:], btcDelBytes) +} + +func (k Keeper) getBTCDelegation(ctx context.Context, stakingTxHash chainhash.Hash) *types.BTCDelegation { + store := k.btcDelegationStore(ctx) + btcDelBytes := store.Get(stakingTxHash[:]) + if len(btcDelBytes) == 0 { + return nil + } + var btcDel types.BTCDelegation + k.cdc.MustUnmarshal(btcDelBytes, &btcDel) + return &btcDel +} + +// btcDelegationStore returns the KVStore of the BTC delegations +// prefix: BTCDelegationKey +// key: BTC delegation's staking tx hash +// value: BTCDelegation +func (k Keeper) btcDelegationStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.BTCDelegationKey) +} diff --git a/x/btcstaking/keeper/btc_delegators.go b/x/btcstaking/keeper/btc_delegators.go new file mode 100644 index 000000000..146b2e7be --- /dev/null +++ b/x/btcstaking/keeper/btc_delegators.go @@ -0,0 +1,153 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/runtime" + + "cosmossdk.io/store/prefix" + "github.com/btcsuite/btcd/chaincfg/chainhash" + + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +// AddBTCDelegation indexes the given BTC delegation in the BTC delegator store, and saves +// it under BTC delegation store +func (k Keeper) AddBTCDelegation(ctx context.Context, btcDel *types.BTCDelegation) error { + if err := btcDel.ValidateBasic(); err != nil { + return err + } + + // get staking tx hash + stakingTxHash, err := btcDel.GetStakingTxHash() + if err != nil { + return err + } + + // for each finality provider the delegation restakes to, update its index + for _, fpBTCPK := range btcDel.FpBtcPkList { + var btcDelIndex = types.NewBTCDelegatorDelegationIndex() + if k.hasBTCDelegatorDelegations(ctx, &fpBTCPK, btcDel.BtcPk) { + btcDelIndex, err = k.getBTCDelegatorDelegationIndex(ctx, &fpBTCPK, btcDel.BtcPk) + if err != nil { + // this can only be a programming error + panic(fmt.Errorf("failed to get BTC delegations while hasBTCDelegatorDelegations returns true")) + } + } + + // index staking tx hash of this BTC delegation + if err := btcDelIndex.Add(stakingTxHash); err != nil { + return types.ErrInvalidStakingTx.Wrapf(err.Error()) + } + // save the index + store := k.btcDelegatorStore(ctx, &fpBTCPK) + delBTCPKBytes := btcDel.BtcPk.MustMarshal() + btcDelIndexBytes := k.cdc.MustMarshal(btcDelIndex) + store.Set(delBTCPKBytes, btcDelIndexBytes) + } + + // save this BTC delegation + k.setBTCDelegation(ctx, btcDel) + + return nil +} + +// IterateBTCDelegations iterates all BTC delegations under a given finality provider +func (k Keeper) IterateBTCDelegations(ctx context.Context, fpBTCPK *bbn.BIP340PubKey, handler func(btcDel *types.BTCDelegation) bool) { + btcDelIter := k.btcDelegatorStore(ctx, fpBTCPK).Iterator(nil, nil) + defer btcDelIter.Close() + for ; btcDelIter.Valid(); btcDelIter.Next() { + // unmarshal delegator's delegation index + var btcDelIndex types.BTCDelegatorDelegationIndex + k.cdc.MustUnmarshal(btcDelIter.Value(), &btcDelIndex) + // retrieve and process each of the BTC delegation + for _, stakingTxHashBytes := range btcDelIndex.StakingTxHashList { + stakingTxHash, err := chainhash.NewHash(stakingTxHashBytes) + if err != nil { + panic(err) // only programming error is possible + } + btcDel := k.getBTCDelegation(ctx, *stakingTxHash) + shouldContinue := handler(btcDel) + if !shouldContinue { + return + } + } + } +} + +// hasBTCDelegatorDelegations checks if the given BTC delegator has any BTC delegations under a given finality provider +func (k Keeper) hasBTCDelegatorDelegations(ctx context.Context, fpBTCPK *bbn.BIP340PubKey, delBTCPK *bbn.BIP340PubKey) bool { + fpBTCPKBytes := fpBTCPK.MustMarshal() + delBTCPKBytes := delBTCPK.MustMarshal() + + if !k.HasFinalityProvider(ctx, fpBTCPKBytes) { + return false + } + store := k.btcDelegatorStore(ctx, fpBTCPK) + return store.Has(delBTCPKBytes) +} + +// getBTCDelegatorDelegationIndex gets the BTC delegation index with a given BTC PK under a given finality provider +func (k Keeper) getBTCDelegatorDelegationIndex(ctx context.Context, fpBTCPK *bbn.BIP340PubKey, delBTCPK *bbn.BIP340PubKey) (*types.BTCDelegatorDelegationIndex, error) { + fpBTCPKBytes := fpBTCPK.MustMarshal() + delBTCPKBytes := delBTCPK.MustMarshal() + store := k.btcDelegatorStore(ctx, fpBTCPK) + + // ensure the finality provider exists + if !k.HasFinalityProvider(ctx, fpBTCPKBytes) { + return nil, types.ErrFpNotFound + } + + // ensure BTC delegator exists + if !store.Has(delBTCPKBytes) { + return nil, types.ErrBTCDelegatorNotFound + } + // get and unmarshal + var btcDelIndex types.BTCDelegatorDelegationIndex + btcDelIndexBytes := store.Get(delBTCPKBytes) + k.cdc.MustUnmarshal(btcDelIndexBytes, &btcDelIndex) + return &btcDelIndex, nil +} + +// getBTCDelegatorDelegations gets the BTC delegations with a given BTC PK under a given finality provider +func (k Keeper) getBTCDelegatorDelegations(ctx context.Context, fpBTCPK *bbn.BIP340PubKey, delBTCPK *bbn.BIP340PubKey) (*types.BTCDelegatorDelegations, error) { + btcDelIndex, err := k.getBTCDelegatorDelegationIndex(ctx, fpBTCPK, delBTCPK) + if err != nil { + return nil, err + } + // get BTC delegation from each staking tx hash + btcDels := []*types.BTCDelegation{} + for _, stakingTxHashBytes := range btcDelIndex.StakingTxHashList { + stakingTxHash, err := chainhash.NewHash(stakingTxHashBytes) + if err != nil { + // failing to unmarshal hash bytes in DB's BTC delegation index is a programming error + panic(err) + } + btcDel := k.getBTCDelegation(ctx, *stakingTxHash) + btcDels = append(btcDels, btcDel) + } + return &types.BTCDelegatorDelegations{Dels: btcDels}, nil +} + +// GetBTCDelegation gets the BTC delegation with a given staking tx hash +func (k Keeper) GetBTCDelegation(ctx context.Context, stakingTxHashStr string) (*types.BTCDelegation, error) { + // decode staking tx hash string + stakingTxHash, err := chainhash.NewHashFromStr(stakingTxHashStr) + if err != nil { + return nil, err + } + + return k.getBTCDelegation(ctx, *stakingTxHash), nil +} + +// btcDelegatorStore returns the KVStore of the BTC delegators +// prefix: BTCDelegatorKey || finality provider's Bitcoin secp256k1 PK +// key: delegator's Bitcoin secp256k1 PK +// value: BTCDelegatorDelegationIndex (a list of BTCDelegations' staking tx hashes) +func (k Keeper) btcDelegatorStore(ctx context.Context, fpBTCPK *bbn.BIP340PubKey) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + delegationStore := prefix.NewStore(storeAdapter, types.BTCDelegatorKey) + return prefix.NewStore(delegationStore, fpBTCPK.MustMarshal()) +} diff --git a/x/btcstaking/keeper/btc_height_index.go b/x/btcstaking/keeper/btc_height_index.go new file mode 100644 index 000000000..9ed5e3961 --- /dev/null +++ b/x/btcstaking/keeper/btc_height_index.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// IndexBTCHeight indexes the current BTC height, and saves it to KVStore +func (k Keeper) IndexBTCHeight(ctx context.Context) { + babylonHeight := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + btcTip := k.btclcKeeper.GetTipInfo(ctx) + if btcTip == nil { + return + } + btcHeight := btcTip.Height + store := k.btcHeightStore(ctx) + store.Set(sdk.Uint64ToBigEndian(babylonHeight), sdk.Uint64ToBigEndian(btcHeight)) +} + +func (k Keeper) GetBTCHeightAtBabylonHeight(ctx context.Context, babylonHeight uint64) (uint64, error) { + store := k.btcHeightStore(ctx) + btcHeightBytes := store.Get(sdk.Uint64ToBigEndian(babylonHeight)) + if len(btcHeightBytes) == 0 { + return 0, types.ErrBTCHeightNotFound + } + return sdk.BigEndianToUint64(btcHeightBytes), nil +} + +func (k Keeper) GetCurrentBTCHeight(ctx context.Context) (uint64, error) { + babylonHeight := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + return k.GetBTCHeightAtBabylonHeight(ctx, babylonHeight) +} + +// btcHeightStore returns the KVStore of the BTC heights +// prefix: BTCHeightKey +// key: Babylon block height +// value: BTC block height +func (k Keeper) btcHeightStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.BTCHeightKey) +} diff --git a/x/btcstaking/keeper/btc_height_index_test.go b/x/btcstaking/keeper/btc_height_index_test.go new file mode 100644 index 000000000..27fdf2bc9 --- /dev/null +++ b/x/btcstaking/keeper/btc_height_index_test.go @@ -0,0 +1,43 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzBTCHeightIndex(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, nil) + + // randomise Babylon height and BTC height + babylonHeight := datagen.RandomInt(r, 100) + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btcHeight := datagen.RandomInt(r, 100) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: btcHeight}).Times(1) + keeper.IndexBTCHeight(ctx) + + // assert BTC height + actualBtcHeight, err := keeper.GetBTCHeightAtBabylonHeight(ctx, babylonHeight) + require.NoError(t, err) + require.Equal(t, btcHeight, actualBtcHeight) + // assert current BTC height + curBtcHeight, err := keeper.GetCurrentBTCHeight(ctx) + require.NoError(t, err) + require.Equal(t, btcHeight, curBtcHeight) + }) +} diff --git a/x/btcstaking/keeper/finality_providers.go b/x/btcstaking/keeper/finality_providers.go new file mode 100644 index 000000000..01b7e0c77 --- /dev/null +++ b/x/btcstaking/keeper/finality_providers.go @@ -0,0 +1,66 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +// SetFinalityProvider adds the given finality provider to KVStore +func (k Keeper) SetFinalityProvider(ctx context.Context, fp *types.FinalityProvider) { + store := k.finalityProviderStore(ctx) + fpBytes := k.cdc.MustMarshal(fp) + store.Set(fp.BtcPk.MustMarshal(), fpBytes) +} + +// HasFinalityProvider checks if the finality provider exists +func (k Keeper) HasFinalityProvider(ctx context.Context, fpBTCPK []byte) bool { + store := k.finalityProviderStore(ctx) + return store.Has(fpBTCPK) +} + +// GetFinalityProvider gets the finality provider with the given finality provider Bitcoin PK +func (k Keeper) GetFinalityProvider(ctx context.Context, fpBTCPK []byte) (*types.FinalityProvider, error) { + store := k.finalityProviderStore(ctx) + if !k.HasFinalityProvider(ctx, fpBTCPK) { + return nil, types.ErrFpNotFound + } + fpBytes := store.Get(fpBTCPK) + var fp types.FinalityProvider + k.cdc.MustUnmarshal(fpBytes, &fp) + return &fp, nil +} + +// SlashFinalityProvider slashes a finality provider with the given PK +// A slashed finality provider will not have voting power +func (k Keeper) SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error { + fp, err := k.GetFinalityProvider(ctx, fpBTCPK) + if err != nil { + return err + } + if fp.IsSlashed() { + return types.ErrFpAlreadySlashed + } + fp.SlashedBabylonHeight = uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + btcTip := k.btclcKeeper.GetTipInfo(ctx) + if btcTip == nil { + panic(fmt.Errorf("failed to get current BTC tip")) + } + fp.SlashedBtcHeight = btcTip.Height + k.SetFinalityProvider(ctx, fp) + return nil +} + +// finalityProviderStore returns the KVStore of the finality provider set +// prefix: FinalityProviderKey +// key: Bitcoin secp256k1 PK +// value: FinalityProvider object +func (k Keeper) finalityProviderStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.FinalityProviderKey) +} diff --git a/x/btcstaking/keeper/grpc_query.go b/x/btcstaking/keeper/grpc_query.go new file mode 100644 index 000000000..d7455786c --- /dev/null +++ b/x/btcstaking/keeper/grpc_query.go @@ -0,0 +1,283 @@ +package keeper + +import ( + "context" + "encoding/hex" + + errorsmod "cosmossdk.io/errors" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/chaincfg/chainhash" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ types.QueryServer = Keeper{} + +// FinalityProviders returns a paginated list of all Babylon maintained finality providers +func (k Keeper) FinalityProviders(ctx context.Context, req *types.QueryFinalityProvidersRequest) (*types.QueryFinalityProvidersResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := k.finalityProviderStore(sdkCtx) + + var finalityProviders []*types.FinalityProvider + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + var finalityProvider types.FinalityProvider + k.cdc.MustUnmarshal(value, &finalityProvider) + finalityProviders = append(finalityProviders, &finalityProvider) + return nil + }) + if err != nil { + return nil, err + } + + return &types.QueryFinalityProvidersResponse{FinalityProviders: finalityProviders, Pagination: pageRes}, nil +} + +// FinalityProvider returns the finality provider with the specified finality provider BTC PK +func (k Keeper) FinalityProvider(ctx context.Context, req *types.QueryFinalityProviderRequest) (*types.QueryFinalityProviderResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if len(req.FpBtcPkHex) == 0 { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, "finality provider BTC public key cannot be empty") + } + + fpPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, err + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + fp, err := k.GetFinalityProvider(sdkCtx, fpPK.MustMarshal()) + + if err != nil { + return nil, err + } + + return &types.QueryFinalityProviderResponse{FinalityProvider: fp}, nil +} + +// BTCDelegations returns all BTC delegations under a given status +func (k Keeper) BTCDelegations(ctx context.Context, req *types.QueryBTCDelegationsRequest) (*types.QueryBTCDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + covenantQuorum := k.GetParams(ctx).CovenantQuorum + + // get current BTC height + btcTipHeight := k.btclcKeeper.GetTipInfo(ctx).Height + // get value of w + wValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + + store := k.btcDelegationStore(ctx) + var btcDels []*types.BTCDelegation + pageRes, err := query.FilteredPaginate(store, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) { + var btcDel types.BTCDelegation + k.cdc.MustUnmarshal(value, &btcDel) + + // hit if the queried status is ANY or matches the BTC delegation status + if req.Status == types.BTCDelegationStatus_ANY || btcDel.GetStatus(btcTipHeight, wValue, covenantQuorum) == req.Status { + if accumulate { + btcDels = append(btcDels, &btcDel) + } + return true, nil + } + + return false, nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryBTCDelegationsResponse{ + BtcDelegations: btcDels, + Pagination: pageRes, + }, nil +} + +// FinalityProviderPowerAtHeight returns the voting power of the specified finality provider +// at the provided Babylon height +func (k Keeper) FinalityProviderPowerAtHeight(ctx context.Context, req *types.QueryFinalityProviderPowerAtHeightRequest) (*types.QueryFinalityProviderPowerAtHeightResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal finality provider BTC PK hex: %v", err) + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + power := k.GetVotingPower(sdkCtx, fpBTCPK.MustMarshal(), req.Height) + + return &types.QueryFinalityProviderPowerAtHeightResponse{VotingPower: power}, nil +} + +// FinalityProviderCurrentPower returns the voting power of the specified finality provider +// at the current height +func (k Keeper) FinalityProviderCurrentPower(ctx context.Context, req *types.QueryFinalityProviderCurrentPowerRequest) (*types.QueryFinalityProviderCurrentPowerResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal finality provider BTC PK hex: %v", err) + } + + height, power := k.GetCurrentVotingPower(ctx, *fpBTCPK) + + return &types.QueryFinalityProviderCurrentPowerResponse{Height: height, VotingPower: power}, nil +} + +// ActiveFinalityProvidersAtHeight returns the active finality providers at the provided height +func (k Keeper) ActiveFinalityProvidersAtHeight(ctx context.Context, req *types.QueryActiveFinalityProvidersAtHeightRequest) (*types.QueryActiveFinalityProvidersAtHeightResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := k.votingPowerStore(sdkCtx, req.Height) + + var finalityProvidersWithMeta []*types.FinalityProviderWithMeta + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + finalityProvider, err := k.GetFinalityProvider(sdkCtx, key) + if err != nil { + return err + } + + votingPower := k.GetVotingPower(sdkCtx, key, req.Height) + if votingPower > 0 { + finalityProviderWithMeta := types.FinalityProviderWithMeta{ + BtcPk: finalityProvider.BtcPk, + Height: req.Height, + VotingPower: votingPower, + SlashedBabylonHeight: finalityProvider.SlashedBabylonHeight, + SlashedBtcHeight: finalityProvider.SlashedBtcHeight, + } + finalityProvidersWithMeta = append(finalityProvidersWithMeta, &finalityProviderWithMeta) + } + + return nil + }) + if err != nil { + return nil, err + } + + return &types.QueryActiveFinalityProvidersAtHeightResponse{FinalityProviders: finalityProvidersWithMeta, Pagination: pageRes}, nil +} + +// ActivatedHeight returns the Babylon height in which the BTC Staking protocol was enabled +// TODO: Requires investigation on whether we can enable the BTC staking protocol at genesis +func (k Keeper) ActivatedHeight(ctx context.Context, req *types.QueryActivatedHeightRequest) (*types.QueryActivatedHeightResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + activatedHeight, err := k.GetBTCStakingActivatedHeight(sdkCtx) + if err != nil { + return nil, err + } + return &types.QueryActivatedHeightResponse{Height: activatedHeight}, nil +} + +// FinalityProviderDelegations returns all the delegations of the provided finality provider filtered by the provided status. +func (k Keeper) FinalityProviderDelegations(ctx context.Context, req *types.QueryFinalityProviderDelegationsRequest) (*types.QueryFinalityProviderDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if len(req.FpBtcPkHex) == 0 { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, "finality provider BTC public key cannot be empty") + } + + fpPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, err + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + btcDelStore := k.btcDelegatorStore(sdkCtx, fpPK) + + btcDels := []*types.BTCDelegatorDelegations{} + pageRes, err := query.Paginate(btcDelStore, req.Pagination, func(key, value []byte) error { + delBTCPK, err := bbn.NewBIP340PubKey(key) + if err != nil { + return err + } + + curBTCDels, err := k.getBTCDelegatorDelegations(sdkCtx, fpPK, delBTCPK) + if err != nil { + return err + } + + btcDels = append(btcDels, curBTCDels) + return nil + }) + if err != nil { + return nil, err + } + + return &types.QueryFinalityProviderDelegationsResponse{BtcDelegatorDelegations: btcDels, Pagination: pageRes}, nil +} + +// BTCDelegation returns existing btc delegation by staking tx hash +func (k Keeper) BTCDelegation(ctx context.Context, req *types.QueryBTCDelegationRequest) (*types.QueryBTCDelegationResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + // decode staking tx hash + stakingTxHash, err := chainhash.NewHashFromStr(req.StakingTxHashHex) + if err != nil { + return nil, err + } + + // find BTC delegation + btcDel := k.getBTCDelegation(ctx, *stakingTxHash) + if btcDel == nil { + return nil, types.ErrBTCDelegationNotFound + } + + // check whether it's active + currentTip := k.btclcKeeper.GetTipInfo(ctx) + currentWValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + isActive := btcDel.GetStatus( + currentTip.Height, + currentWValue, + k.GetParams(ctx).CovenantQuorum, + ) == types.BTCDelegationStatus_ACTIVE + + // get its undelegation info + undelegationInfo := &types.BTCUndelegationInfo{ + UnbondingTx: btcDel.BtcUndelegation.UnbondingTx, + CovenantUnbondingSigList: btcDel.BtcUndelegation.CovenantUnbondingSigList, + CovenantSlashingSigs: btcDel.BtcUndelegation.CovenantSlashingSigs, + } + + return &types.QueryBTCDelegationResponse{ + BtcPk: btcDel.BtcPk, + FpBtcPkList: btcDel.FpBtcPkList, + StartHeight: btcDel.StartHeight, + EndHeight: btcDel.EndHeight, + TotalSat: btcDel.TotalSat, + StakingTxHex: hex.EncodeToString(btcDel.StakingTx), + CovenantSigs: btcDel.CovenantSigs, + Active: isActive, + UnbondingTime: btcDel.UnbondingTime, + UndelegationInfo: undelegationInfo, + }, nil +} diff --git a/x/btcstaking/keeper/grpc_query_test.go b/x/btcstaking/keeper/grpc_query_test.go new file mode 100644 index 000000000..353a1a910 --- /dev/null +++ b/x/btcstaking/keeper/grpc_query_test.go @@ -0,0 +1,578 @@ +package keeper_test + +import ( + "errors" + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/btcsuite/btcd/chaincfg" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +func FuzzActivatedHeight(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // not activated yet + _, err := keeper.GetBTCStakingActivatedHeight(ctx) + require.Error(t, err) + + randomActivatedHeight := datagen.RandomInt(r, 100) + 1 + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), randomActivatedHeight, uint64(10)) + + // now it's activated + resp, err := keeper.ActivatedHeight(ctx, &types.QueryActivatedHeightRequest{}) + require.NoError(t, err) + require.Equal(t, randomActivatedHeight, resp.Height) + }) +} + +func FuzzFinalityProviders(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // Generate random finality providers and add them to kv store + fpsMap := make(map[string]*types.FinalityProvider) + for i := 0; i < int(datagen.RandomInt(r, 10)+1); i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + + keeper.SetFinalityProvider(ctx, fp) + fpsMap[fp.BtcPk.MarshalHex()] = fp + } + numOfFpsInStore := len(fpsMap) + + // Test nil request + resp, err := keeper.FinalityProviders(ctx, nil) + if resp != nil { + t.Errorf("Nil input led to a non-nil response") + } + if err == nil { + t.Errorf("Nil input led to a nil error") + } + + // Generate a page request with a limit and a nil key + limit := datagen.RandomInt(r, numOfFpsInStore) + 1 + pagination := constructRequestWithLimit(r, limit) + // Generate the initial query + req := types.QueryFinalityProvidersRequest{Pagination: pagination} + // Construct a mapping from the finality providers found to a boolean value + // Will be used later to evaluate whether all the finality providers were returned + fpsFound := make(map[string]bool, 0) + + for i := uint64(0); i < uint64(numOfFpsInStore); i += limit { + resp, err = keeper.FinalityProviders(ctx, &req) + if err != nil { + t.Errorf("Valid request led to an error %s", err) + } + if resp == nil { + t.Fatalf("Valid request led to a nil response") + } + + for _, fp := range resp.FinalityProviders { + // Check if the pk exists in the map + if _, ok := fpsMap[fp.BtcPk.MarshalHex()]; !ok { + t.Fatalf("rpc returned a finality provider that was not created") + } + fpsFound[fp.BtcPk.MarshalHex()] = true + } + + // Construct the next page request + pagination = constructRequestWithKeyAndLimit(r, resp.Pagination.NextKey, limit) + req = types.QueryFinalityProvidersRequest{Pagination: pagination} + } + + if len(fpsFound) != len(fpsMap) { + t.Errorf("Some finality providers were missed. Got %d while %d were expected", len(fpsFound), len(fpsMap)) + } + }) +} + +func FuzzFinalityProvider(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // Generate random finality providers and add them to kv store + fpsMap := make(map[string]*types.FinalityProvider) + for i := 0; i < int(datagen.RandomInt(r, 10)+1); i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + + keeper.SetFinalityProvider(ctx, fp) + fpsMap[fp.BtcPk.MarshalHex()] = fp + } + + // Test nil request + resp, err := keeper.FinalityProvider(ctx, nil) + require.Error(t, err) + require.Nil(t, resp) + + for k, v := range fpsMap { + // Generate a request with a valid key + req := types.QueryFinalityProviderRequest{FpBtcPkHex: k} + resp, err := keeper.FinalityProvider(ctx, &req) + if err != nil { + t.Errorf("Valid request led to an error %s", err) + } + if resp == nil { + t.Fatalf("Valid request led to a nil response") + } + + // check keys from map matches those in returned response + require.Equal(t, v.BtcPk.MarshalHex(), resp.FinalityProvider.BtcPk.MarshalHex()) + require.Equal(t, v.BabylonPk, resp.FinalityProvider.BabylonPk) + } + + // check some random non-existing guy + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + req := types.QueryFinalityProviderRequest{FpBtcPkHex: fp.BtcPk.MarshalHex()} + respNonExists, err := keeper.FinalityProvider(ctx, &req) + require.Error(t, err) + require.Nil(t, respNonExists) + require.True(t, errors.Is(err, types.ErrFpNotFound)) + }) +} + +func FuzzPendingBTCDelegations(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Setup keeper and context + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // Generate a random number of finality providers + numFps := datagen.RandomInt(r, 5) + 1 + fps := []*types.FinalityProvider{} + for i := uint64(0); i < numFps; i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + fps = append(fps, fp) + } + + // Generate a random number of BTC delegations under each finality provider + startHeight := datagen.RandomInt(r, 100) + 1 + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: startHeight}).AnyTimes() + + endHeight := datagen.RandomInt(r, 1000) + startHeight + btcctypes.DefaultParams().CheckpointFinalizationTimeout + 1 + numBTCDels := datagen.RandomInt(r, 10) + 1 + pendingBtcDelsMap := make(map[string]*types.BTCDelegation) + for _, fp := range fps { + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fp.BtcPk}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + startHeight, endHeight, 10000, + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + if datagen.RandomInt(r, 2) == 1 { + // remove covenant sig in random BTC delegations to make them inactive + btcDel.CovenantSigs = nil + pendingBtcDelsMap[btcDel.BtcPk.MarshalHex()] = btcDel + } + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + + txHash := btcDel.MustGetStakingTxHash().String() + delView, err := keeper.BTCDelegation(ctx, &types.QueryBTCDelegationRequest{ + StakingTxHashHex: txHash, + }) + require.NoError(t, err) + require.NotNil(t, delView) + } + } + + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + + // querying paginated BTC delegations and assert + // Generate a page request with a limit and a nil key + if len(pendingBtcDelsMap) == 0 { + return + } + limit := datagen.RandomInt(r, len(pendingBtcDelsMap)) + 1 + pagination := constructRequestWithLimit(r, limit) + req := &types.QueryBTCDelegationsRequest{ + Status: types.BTCDelegationStatus_PENDING, + Pagination: pagination, + } + for i := uint64(0); i < numBTCDels; i += limit { + resp, err := keeper.BTCDelegations(ctx, req) + require.NoError(t, err) + require.NotNil(t, resp) + for _, btcDel := range resp.BtcDelegations { + _, ok := pendingBtcDelsMap[btcDel.BtcPk.MarshalHex()] + require.True(t, ok) + } + // Construct the next page request + pagination.Key = resp.Pagination.NextKey + } + }) +} + +func FuzzFinalityProviderPowerAtHeight(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + + // random finality provider + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + // add this finality provider + keeper.SetFinalityProvider(ctx, fp) + // set random voting power at random height + randomHeight := datagen.RandomInt(r, 100) + 1 + randomPower := datagen.RandomInt(r, 100) + 1 + keeper.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), randomHeight, randomPower) + + req := &types.QueryFinalityProviderPowerAtHeightRequest{ + FpBtcPkHex: fp.BtcPk.MarshalHex(), + Height: randomHeight, + } + resp, err := keeper.FinalityProviderPowerAtHeight(ctx, req) + require.NoError(t, err) + require.Equal(t, randomPower, resp.VotingPower) + }) +} + +func FuzzFinalityProviderCurrentVotingPower(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + + // random finality provider + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + // add this finality provider + keeper.SetFinalityProvider(ctx, fp) + // set random voting power at random height + randomHeight := datagen.RandomInt(r, 100) + 1 + ctx = datagen.WithCtxHeight(ctx, randomHeight) + randomPower := datagen.RandomInt(r, 100) + 1 + keeper.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), randomHeight, randomPower) + + // assert voting power at current height + req := &types.QueryFinalityProviderCurrentPowerRequest{ + FpBtcPkHex: fp.BtcPk.MarshalHex(), + } + resp, err := keeper.FinalityProviderCurrentPower(ctx, req) + require.NoError(t, err) + require.Equal(t, randomHeight, resp.Height) + require.Equal(t, randomPower, resp.VotingPower) + + // if height increments but voting power hasn't recorded yet, then + // we need to return the height and voting power at the last height + ctx = datagen.WithCtxHeight(ctx, randomHeight+1) + resp, err = keeper.FinalityProviderCurrentPower(ctx, req) + require.NoError(t, err) + require.Equal(t, randomHeight, resp.Height) + require.Equal(t, randomPower, resp.VotingPower) + + // test the case when the finality provider has 0 voting power + ctx = datagen.WithCtxHeight(ctx, randomHeight+2) + keeper.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), randomHeight+2, 0) + resp, err = keeper.FinalityProviderCurrentPower(ctx, req) + require.NoError(t, err) + require.Equal(t, randomHeight+2, resp.Height) + require.Equal(t, uint64(0), resp.VotingPower) + }) +} + +func FuzzActiveFinalityProvidersAtHeight(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // Generate a random batch of finality providers + var fps []*types.FinalityProvider + numFpsWithVotingPower := datagen.RandomInt(r, 10) + 1 + numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) + for i := uint64(0); i < numFps; i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + fps = append(fps, fp) + } + + // For numFpsWithVotingPower finality providers, generate a random number of BTC delegations + numBTCDels := datagen.RandomInt(r, 10) + 1 + babylonHeight := datagen.RandomInt(r, 10) + 1 + fpsWithVotingPowerMap := make(map[string]*types.FinalityProvider) + for i := uint64(0); i < numFpsWithVotingPower; i++ { + fpBTCPK := fps[i].BtcPk + fpsWithVotingPowerMap[fpBTCPK.MarshalHex()] = fps[i] + + var totalVotingPower uint64 + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fpBTCPK}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, 10000, + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + totalVotingPower += btcDel.TotalSat + } + + keeper.SetVotingPower(ctx, fpBTCPK.MustMarshal(), babylonHeight, totalVotingPower) + } + + // Test nil request + resp, err := keeper.ActiveFinalityProvidersAtHeight(ctx, nil) + if resp != nil { + t.Errorf("Nil input led to a non-nil response") + } + if err == nil { + t.Errorf("Nil input led to a nil error") + } + + // Generate a page request with a limit and a nil key + limit := datagen.RandomInt(r, int(numFpsWithVotingPower)) + 1 + pagination := constructRequestWithLimit(r, limit) + // Generate the initial query + req := types.QueryActiveFinalityProvidersAtHeightRequest{Height: babylonHeight, Pagination: pagination} + // Construct a mapping from the finality providers found to a boolean value + // Will be used later to evaluate whether all the finality providers were returned + fpsFound := make(map[string]bool, 0) + + for i := uint64(0); i < numFpsWithVotingPower; i += limit { + resp, err = keeper.ActiveFinalityProvidersAtHeight(ctx, &req) + if err != nil { + t.Errorf("Valid request led to an error %s", err) + } + if resp == nil { + t.Fatalf("Valid request led to a nil response") + } + + for _, fp := range resp.FinalityProviders { + // Check if the pk exists in the map + if _, ok := fpsWithVotingPowerMap[fp.BtcPk.MarshalHex()]; !ok { + t.Fatalf("rpc returned a finality provider that was not created") + } + fpsFound[fp.BtcPk.MarshalHex()] = true + } + + // Construct the next page request + pagination = constructRequestWithKeyAndLimit(r, resp.Pagination.NextKey, limit) + req = types.QueryActiveFinalityProvidersAtHeightRequest{Height: babylonHeight, Pagination: pagination} + } + + if len(fpsFound) != len(fpsWithVotingPowerMap) { + t.Errorf("Some finality providers were missed. Got %d while %d were expected", len(fpsFound), len(fpsWithVotingPowerMap)) + } + }) +} + +func FuzzFinalityProviderDelegations(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Setup keeper and context + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := testkeeper.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // Generate a finality provider + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + + startHeight := datagen.RandomInt(r, 100) + 1 + endHeight := datagen.RandomInt(r, 1000) + startHeight + btcctypes.DefaultParams().CheckpointFinalizationTimeout + 1 + // Generate a random number of BTC delegations under this finality provider + numBTCDels := datagen.RandomInt(r, 10) + 1 + expectedBtcDelsMap := make(map[string]*types.BTCDelegation) + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fp.BtcPk}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + startHeight, endHeight, 10000, + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + expectedBtcDelsMap[btcDel.BtcPk.MarshalHex()] = btcDel + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + } + + // Test nil request + resp, err := keeper.FinalityProviderDelegations(ctx, nil) + require.Nil(t, resp) + require.Error(t, err) + + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: startHeight}).Times(1) + keeper.IndexBTCHeight(ctx) + + // Generate a page request with a limit and a nil key + // query a page of BTC delegations and assert consistency + limit := datagen.RandomInt(r, len(expectedBtcDelsMap)) + 1 + pagination := constructRequestWithLimit(r, limit) + // Generate the initial query + req := types.QueryFinalityProviderDelegationsRequest{ + FpBtcPkHex: fp.BtcPk.MarshalHex(), + Pagination: pagination, + } + // Construct a mapping from the finality providers found to a boolean value + // Will be used later to evaluate whether all the finality providers were returned + btcDelsFound := make(map[string]bool, 0) + + for i := uint64(0); i < numBTCDels; i += limit { + resp, err = keeper.FinalityProviderDelegations(ctx, &req) + require.NoError(t, err) + require.NotNil(t, resp) + for _, btcDels := range resp.BtcDelegatorDelegations { + require.Len(t, btcDels.Dels, 1) + btcDel := btcDels.Dels[0] + require.Equal(t, fp.BtcPk, &btcDel.FpBtcPkList[0]) + // Check if the pk exists in the map + _, ok := expectedBtcDelsMap[btcDel.BtcPk.MarshalHex()] + require.True(t, ok) + btcDelsFound[btcDel.BtcPk.MarshalHex()] = true + } + // Construct the next page request + pagination = constructRequestWithKeyAndLimit(r, resp.Pagination.NextKey, limit) + req = types.QueryFinalityProviderDelegationsRequest{ + FpBtcPkHex: fp.BtcPk.MarshalHex(), + Pagination: pagination, + } + } + require.Equal(t, len(btcDelsFound), len(expectedBtcDelsMap)) + + }) +} + +// Constructors for PageRequest objects +func constructRequestWithKeyAndLimit(r *rand.Rand, key []byte, limit uint64) *query.PageRequest { + // If limit is 0, set one randomly + if limit == 0 { + limit = uint64(r.Int63() + 1) // Use Int63 instead of Uint64 to avoid overflows + } + return &query.PageRequest{ + Key: key, + Offset: 0, // only offset or key is set + Limit: limit, + CountTotal: false, // only used when offset is used + Reverse: false, + } +} + +func constructRequestWithLimit(r *rand.Rand, limit uint64) *query.PageRequest { + return constructRequestWithKeyAndLimit(r, nil, limit) +} diff --git a/x/btcstaking/keeper/incentive.go b/x/btcstaking/keeper/incentive.go new file mode 100644 index 000000000..7c62d2428 --- /dev/null +++ b/x/btcstaking/keeper/incentive.go @@ -0,0 +1,40 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) setRewardDistCache(ctx context.Context, height uint64, rdc *types.RewardDistCache) { + store := k.rewardDistCacheStore(ctx) + store.Set(sdk.Uint64ToBigEndian(height), k.cdc.MustMarshal(rdc)) +} + +func (k Keeper) GetRewardDistCache(ctx context.Context, height uint64) (*types.RewardDistCache, error) { + store := k.rewardDistCacheStore(ctx) + rdcBytes := store.Get(sdk.Uint64ToBigEndian(height)) + if len(rdcBytes) == 0 { + return nil, types.ErrRewardDistCacheNotFound + } + var rdc types.RewardDistCache + k.cdc.MustUnmarshal(rdcBytes, &rdc) + return &rdc, nil +} + +func (k Keeper) RemoveRewardDistCache(ctx context.Context, height uint64) { + store := k.rewardDistCacheStore(ctx) + store.Delete(sdk.Uint64ToBigEndian(height)) +} + +// rewardDistCacheStore returns the KVStore of the reward distribution cache +// prefix: RewardDistCacheKey +// key: Babylon block height +// value: RewardDistCache +func (k Keeper) rewardDistCacheStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.RewardDistCacheKey) +} diff --git a/x/btcstaking/keeper/incentive_test.go b/x/btcstaking/keeper/incentive_test.go new file mode 100644 index 000000000..057461e44 --- /dev/null +++ b/x/btcstaking/keeper/incentive_test.go @@ -0,0 +1,108 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzRecordRewardDistCache(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // generate a random batch of finality providers + numFpsWithVotingPower := datagen.RandomInt(r, 10) + 2 + numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) + fpsWithVotingPowerMap := map[string]*types.FinalityProvider{} + for i := uint64(0); i < numFps; i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + if i < numFpsWithVotingPower { + // these finality providers will receive BTC delegations and have voting power + fpsWithVotingPowerMap[fp.BabylonPk.String()] = fp + } + } + + // for the first numFpsWithVotingPower finality providers, generate a random number of BTC delegations + numBTCDels := datagen.RandomInt(r, 10) + 1 + stakingValue := datagen.RandomInt(r, 100000) + 100000 + for _, fp := range fpsWithVotingPowerMap { + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fp.BtcPk}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, stakingValue, + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + } + } + + // record reward distribution cache + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + // assert reward distribution cache is correct + rdc, err := keeper.GetRewardDistCache(ctx, babylonHeight) + require.NoError(t, err) + require.Equal(t, rdc.TotalVotingPower, numFpsWithVotingPower*numBTCDels*stakingValue) + for _, fpDistInfo := range rdc.FinalityProviders { + require.Equal(t, fpDistInfo.TotalVotingPower, numBTCDels*stakingValue) + fp, ok := fpsWithVotingPowerMap[fpDistInfo.BabylonPk.String()] + require.True(t, ok) + require.Equal(t, fpDistInfo.Commission, fp.Commission) + require.Len(t, fpDistInfo.BtcDels, int(numBTCDels)) + for _, delDistInfo := range fpDistInfo.BtcDels { + require.Equal(t, delDistInfo.VotingPower, stakingValue) + } + } + }) +} diff --git a/x/btcstaking/keeper/keeper.go b/x/btcstaking/keeper/keeper.go new file mode 100644 index 000000000..64197cc54 --- /dev/null +++ b/x/btcstaking/keeper/keeper.go @@ -0,0 +1,120 @@ +package keeper + +import ( + "context" + "fmt" + + corestoretypes "cosmossdk.io/core/store" + + "cosmossdk.io/log" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ( + Keeper struct { + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService + + btclcKeeper types.BTCLightClientKeeper + btccKeeper types.BtcCheckpointKeeper + + btcNet *chaincfg.Params + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string + } +) + +func NewKeeper( + cdc codec.BinaryCodec, + storeService corestoretypes.KVStoreService, + + btclcKeeper types.BTCLightClientKeeper, + btccKeeper types.BtcCheckpointKeeper, + + btcNet *chaincfg.Params, + authority string, +) Keeper { + return Keeper{ + cdc: cdc, + storeService: storeService, + + btclcKeeper: btclcKeeper, + btccKeeper: btccKeeper, + + btcNet: btcNet, + authority: authority, + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// BeginBlocker is invoked upon `BeginBlock` of the system. The function +// iterates over all BTC delegations under non-slashed finality providers +// to 1) record the voting power table for the current height, and 2) record +// the reward distribution cache used for distributing rewards once the block +// is finalised by finality providers. +func (k Keeper) BeginBlocker(ctx context.Context) error { + // index BTC height at the current height + k.IndexBTCHeight(ctx) + + covenantQuorum := k.GetParams(ctx).CovenantQuorum + btcTipHeight, err := k.GetCurrentBTCHeight(ctx) + if err != nil { + panic(err) // only possible upon programming error + } + wValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + + // prepare for recording finality providers with positive voting power + activeFps := []*types.FinalityProviderWithMeta{} + // prepare for recording finality providers and their BTC delegations + // for rewards + rdc := types.NewRewardDistCache() + + // iterate over all finality providers to find out non-slashed ones that have + // positive voting power + k.IterateActiveFPs( + ctx, + func(fp *types.FinalityProvider) bool { + fpDistInfo := types.NewFinalityProviderDistInfo(fp) + + // iterate over all BTC delegations under the finality provider + // in order to accumulate voting power and reward dist info for it + k.IterateBTCDelegations(ctx, fp.BtcPk, func(btcDel *types.BTCDelegation) bool { + // accumulate voting power and reward distribution cache + fpDistInfo.AddBTCDel(btcDel, btcTipHeight, wValue, covenantQuorum) + return true + }) + + if fpDistInfo.TotalVotingPower > 0 { + activeFP := &types.FinalityProviderWithMeta{ + BtcPk: fp.BtcPk, + VotingPower: fpDistInfo.TotalVotingPower, + } + activeFps = append(activeFps, activeFP) + rdc.AddFinalityProviderDistInfo(fpDistInfo) + } + + return true + }, + ) + + // filter out top `MaxActiveFinalityProviders` active finality providers in terms of voting power + activeFps = types.FilterTopNFinalityProviders(activeFps, k.GetParams(ctx).MaxActiveFinalityProviders) + // set voting power table + babylonTipHeight := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + for _, fp := range activeFps { + k.SetVotingPower(ctx, fp.BtcPk.MustMarshal(), babylonTipHeight, fp.VotingPower) + } + + // set the reward distribution cache of the current height + // TODO: only give rewards to top N finality providers and their BTC delegations + k.setRewardDistCache(ctx, uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height), rdc) + + return nil +} diff --git a/x/btcstaking/keeper/keeper_test.go b/x/btcstaking/keeper/keeper_test.go new file mode 100644 index 000000000..dbaebda33 --- /dev/null +++ b/x/btcstaking/keeper/keeper_test.go @@ -0,0 +1,409 @@ +package keeper_test + +import ( + "context" + "math/rand" + "testing" + + "cosmossdk.io/core/header" + sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +type Helper struct { + t testing.TB + + Ctx context.Context + BTCStakingKeeper *keeper.Keeper + BTCLightClientKeeper *types.MockBTCLightClientKeeper + BTCCheckpointKeeper *types.MockBtcCheckpointKeeper + MsgServer types.MsgServer + Net *chaincfg.Params +} + +func NewHelper(t testing.TB, btclcKeeper *types.MockBTCLightClientKeeper, btccKeeper *types.MockBtcCheckpointKeeper) *Helper { + k, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + ctx = ctx.WithHeaderInfo(header.Info{Height: 1}) + msgSrvr := keeper.NewMsgServerImpl(*k) + + return &Helper{ + t: t, + Ctx: ctx, + BTCStakingKeeper: k, + BTCLightClientKeeper: btclcKeeper, + BTCCheckpointKeeper: btccKeeper, + MsgServer: msgSrvr, + Net: &chaincfg.SimNetParams, + } +} + +func (h *Helper) NoError(err error) { + require.NoError(h.t, err) +} + +func (h *Helper) GenAndApplyParams(r *rand.Rand) ([]*btcec.PrivateKey, []*btcec.PublicKey) { + return h.GenAndApplyCustomParams(r, 100, 0) +} + +func (h *Helper) GenAndApplyCustomParams( + r *rand.Rand, + finalizationTimeout uint64, + minUnbondingTime uint32, +) ([]*btcec.PrivateKey, []*btcec.PublicKey) { + // mocking stuff for BTC checkpoint keeper + h.BTCCheckpointKeeper.EXPECT().GetPowLimit().Return(h.Net.PowLimit).AnyTimes() + + params := btcctypes.DefaultParams() + params.CheckpointFinalizationTimeout = finalizationTimeout + + h.BTCCheckpointKeeper.EXPECT().GetParams(gomock.Any()).Return(params).AnyTimes() + + // randomise covenant committee + covenantSKs, covenantPKs, err := datagen.GenRandomBTCKeyPairs(r, 5) + h.NoError(err) + slashingAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + h.NoError(err) + err = h.BTCStakingKeeper.SetParams(h.Ctx, types.Params{ + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: 3, + SlashingAddress: slashingAddress.EncodeAddress(), + MinSlashingTxFeeSat: 10, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"), + SlashingRate: sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2), + MaxActiveFinalityProviders: 100, + MinUnbondingTime: minUnbondingTime, + }) + h.NoError(err) + return covenantSKs, covenantPKs +} + +func CreateFinalityProvider(r *rand.Rand, t *testing.T) *types.FinalityProvider { + fpSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + fp, err := datagen.GenRandomFinalityProviderWithBTCSK(r, fpSK) + require.NoError(t, err) + + return &types.FinalityProvider{ + Description: fp.Description, + Commission: fp.Commission, + BabylonPk: fp.BabylonPk, + BtcPk: fp.BtcPk, + Pop: fp.Pop, + } +} + +func (h *Helper) CreateFinalityProvider(r *rand.Rand) (*btcec.PrivateKey, *btcec.PublicKey, *types.FinalityProvider) { + fpSK, fpPK, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + fp, err := datagen.GenRandomFinalityProviderWithBTCSK(r, fpSK) + h.NoError(err) + msgNewFp := types.MsgCreateFinalityProvider{ + Signer: datagen.GenRandomAccount().Address, + Description: fp.Description, + Commission: fp.Commission, + BabylonPk: fp.BabylonPk, + BtcPk: fp.BtcPk, + Pop: fp.Pop, + } + _, err = h.MsgServer.CreateFinalityProvider(h.Ctx, &msgNewFp) + h.NoError(err) + return fpSK, fpPK, fp +} + +func (h *Helper) CreateDelegationCustom( + r *rand.Rand, + fpPK *btcec.PublicKey, + changeAddress string, + stakingValue int64, + stakingTime uint16, + unbondingTime uint16, +) (string, *btcec.PrivateKey, *btcec.PublicKey, *types.MsgCreateBTCDelegation, error) { + delSK, delPK, err := datagen.GenRandomBTCKeyPair(r) + h.NoError(err) + stakingTimeBlocks := stakingTime + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + covPKs, err := bbn.NewBTCPKsFromBIP340PKs(bsParams.CovenantPks) + h.NoError(err) + + testStakingInfo := datagen.GenBTCStakingSlashingInfo( + r, + h.t, + h.Net, + delSK, + []*btcec.PublicKey{fpPK}, + covPKs, + bsParams.CovenantQuorum, + stakingTimeBlocks, + stakingValue, + bsParams.SlashingAddress, + bsParams.SlashingRate, + unbondingTime, + ) + h.NoError(err) + stakingTxHash := testStakingInfo.StakingTx.TxHash().String() + + // random signer + signer := datagen.GenRandomAccount().Address + // random Babylon SK + delBabylonSK, delBabylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + h.NoError(err) + // PoP + pop, err := types.NewPoP(delBabylonSK, delSK) + h.NoError(err) + // generate staking tx info + prevBlock, _ := datagen.GenRandomBtcdBlock(r, 0, nil) + btcHeaderWithProof := datagen.CreateBlockWithTransaction(r, &prevBlock.Header, testStakingInfo.StakingTx) + btcHeader := btcHeaderWithProof.HeaderBytes + serializedStakingTx, err := bbn.SerializeBTCTx(testStakingInfo.StakingTx) + h.NoError(err) + + txInfo := btcctypes.NewTransactionInfo(&btcctypes.TransactionKey{Index: 1, Hash: btcHeader.Hash()}, serializedStakingTx, btcHeaderWithProof.SpvProof.MerkleNodes) + + // mock for testing k-deep stuff + h.BTCLightClientKeeper.EXPECT().GetHeaderByHash(gomock.Any(), gomock.Eq(btcHeader.Hash())).Return(&btclctypes.BTCHeaderInfo{Header: &btcHeader, Height: 10}).AnyTimes() + h.BTCLightClientKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 30}).AnyTimes() + + slashingSpendInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() + h.NoError(err) + + // generate proper delegator sig + delegatorSig, err := testStakingInfo.SlashingTx.Sign( + testStakingInfo.StakingTx, + 0, + slashingSpendInfo.GetPkScriptPath(), + delSK, + ) + h.NoError(err) + + stakerPk := delSK.PubKey() + stPk := bbn.NewBIP340PubKeyFromBTCPK(stakerPk) + + /* + logics related to on-demand unbonding + */ + stkTxHash := testStakingInfo.StakingTx.TxHash() + stkOutputIdx := uint32(0) + + unbondingValue := stakingValue - 1000 + testUnbondingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + h.t, + h.Net, + delSK, + []*btcec.PublicKey{fpPK}, + covPKs, + bsParams.CovenantQuorum, + wire.NewOutPoint(&stkTxHash, stkOutputIdx), + unbondingTime, + unbondingValue, + bsParams.SlashingAddress, + bsParams.SlashingRate, + unbondingTime, + ) + h.NoError(err) + + delSlashingTxSig, err := testUnbondingInfo.GenDelSlashingTxSig(delSK) + h.NoError(err) + + serializedUnbondingTx, err := bbn.SerializeBTCTx(testUnbondingInfo.UnbondingTx) + h.NoError(err) + + // all good, construct and send MsgCreateBTCDelegation message + msgCreateBTCDel := &types.MsgCreateBTCDelegation{ + Signer: signer, + BabylonPk: delBabylonPK.(*secp256k1.PubKey), + BtcPk: stPk, + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + Pop: pop, + StakingTime: uint32(stakingTimeBlocks), + StakingValue: stakingValue, + StakingTx: txInfo, + SlashingTx: testStakingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + UnbondingTx: serializedUnbondingTx, + UnbondingTime: uint32(unbondingTime), + UnbondingValue: unbondingValue, + UnbondingSlashingTx: testUnbondingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: delSlashingTxSig, + } + + _, err = h.MsgServer.CreateBTCDelegation(h.Ctx, msgCreateBTCDel) + + if err != nil { + return "", nil, nil, nil, err + } + + return stakingTxHash, delSK, delPK, msgCreateBTCDel, nil +} + +func (h *Helper) CreateDelegation( + r *rand.Rand, + fpPK *btcec.PublicKey, + changeAddress string, + stakingValue int64, + stakingTime uint16, +) (string, *btcec.PrivateKey, *btcec.PublicKey, *types.MsgCreateBTCDelegation) { + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + bcParams := h.BTCCheckpointKeeper.GetParams(h.Ctx) + + minUnbondingTime := types.MinimumUnbondingTime( + bsParams, + bcParams, + ) + + stakingTxHash, delSK, delPK, msgCreateBTCDel, err := h.CreateDelegationCustom( + r, + fpPK, + changeAddress, + stakingValue, + stakingTime, + uint16(minUnbondingTime)+1, + ) + + h.NoError(err) + + return stakingTxHash, delSK, delPK, msgCreateBTCDel +} + +func (h *Helper) GenerateCovenantSignaturesMessages( + r *rand.Rand, + covenantSKs []*btcec.PrivateKey, + msgCreateBTCDel *types.MsgCreateBTCDelegation, + del *types.BTCDelegation, +) []*types.MsgAddCovenantSigs { + stakingTx, err := bbn.NewBTCTxFromBytes(del.StakingTx) + h.NoError(err) + stakingTxHash := stakingTx.TxHash().String() + + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + + vPKs, err := bbn.NewBTCPKsFromBIP340PKs(del.FpBtcPkList) + h.NoError(err) + + stakingInfo, err := del.GetStakingInfo(&bsParams, h.Net) + h.NoError(err) + + unbondingPathInfo, err := stakingInfo.UnbondingPathSpendInfo() + h.NoError(err) + slashingPathInfo, err := stakingInfo.SlashingPathSpendInfo() + h.NoError(err) + + // generate all covenant signatures from all covenant members + covenantSlashingTxSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + vPKs, + stakingTx, + slashingPathInfo.GetPkScriptPath(), + msgCreateBTCDel.SlashingTx, + ) + h.NoError(err) + + /* + Logics about on-demand unbonding + */ + + // slash unbonding tx spends unbonding tx + unbondingTx, err := bbn.NewBTCTxFromBytes(del.BtcUndelegation.UnbondingTx) + h.NoError(err) + unbondingInfo, err := del.GetUnbondingInfo(&bsParams, h.Net) + h.NoError(err) + unbondingSlashingPathInfo, err := unbondingInfo.SlashingPathSpendInfo() + h.NoError(err) + + // generate all covenant signatures from all covenant members + covenantUnbondingSlashingTxSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + vPKs, + unbondingTx, + unbondingSlashingPathInfo.GetPkScriptPath(), + del.BtcUndelegation.SlashingTx, + ) + h.NoError(err) + + // each covenant member submits signatures + covUnbondingSigs, err := datagen.GenCovenantUnbondingSigs(covenantSKs, stakingTx, del.StakingOutputIdx, unbondingPathInfo.GetPkScriptPath(), unbondingTx) + h.NoError(err) + + msgs := make([]*types.MsgAddCovenantSigs, len(bsParams.CovenantPks)) + + for i := 0; i < len(bsParams.CovenantPks); i++ { + msgAddCovenantSig := &types.MsgAddCovenantSigs{ + Signer: msgCreateBTCDel.Signer, + Pk: covenantSlashingTxSigs[i].CovPk, + StakingTxHash: stakingTxHash, + SlashingTxSigs: covenantSlashingTxSigs[i].AdaptorSigs, + UnbondingTxSig: bbn.NewBIP340SignatureFromBTCSig(covUnbondingSigs[i]), + SlashingUnbondingTxSigs: covenantUnbondingSlashingTxSigs[i].AdaptorSigs, + } + msgs[i] = msgAddCovenantSig + } + return msgs +} + +func (h *Helper) CreateCovenantSigs( + r *rand.Rand, + covenantSKs []*btcec.PrivateKey, + msgCreateBTCDel *types.MsgCreateBTCDelegation, + del *types.BTCDelegation, +) { + stakingTx, err := bbn.NewBTCTxFromBytes(del.StakingTx) + stakingTxHash := stakingTx.TxHash().String() + + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + + h.NoError(err) + covenantMsgs := h.GenerateCovenantSignaturesMessages(r, covenantSKs, msgCreateBTCDel, del) + for _, msg := range covenantMsgs { + msgCopy := msg + _, err := h.MsgServer.AddCovenantSigs(h.Ctx, msgCopy) + h.NoError(err) + } + /* + ensure covenant sig is added successfully + */ + actualDelWithCovenantSigs, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.Equal(h.t, len(actualDelWithCovenantSigs.CovenantSigs), int(bsParams.CovenantQuorum)) + require.True(h.t, actualDelWithCovenantSigs.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + + require.NotNil(h.t, actualDelWithCovenantSigs.BtcUndelegation) + require.NotNil(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantSlashingSigs) + require.NotNil(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantUnbondingSigList) + require.Len(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantUnbondingSigList, int(bsParams.CovenantQuorum)) + require.Len(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantSlashingSigs, int(bsParams.CovenantQuorum)) + require.Len(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantSlashingSigs[0].AdaptorSigs, 1) + +} + +func (h *Helper) GetDelegationAndCheckValues( + r *rand.Rand, + msgCreateBTCDel *types.MsgCreateBTCDelegation, + fpPK *btcec.PublicKey, + delegatorPK *btcec.PublicKey, + stakingTxHash string, +) *types.BTCDelegation { + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.Equal(h.t, msgCreateBTCDel.BabylonPk, actualDel.BabylonPk) + require.Equal(h.t, msgCreateBTCDel.Pop, actualDel.Pop) + require.Equal(h.t, msgCreateBTCDel.StakingTx.Transaction, actualDel.StakingTx) + require.Equal(h.t, msgCreateBTCDel.SlashingTx, actualDel.SlashingTx) + // ensure the BTC delegation in DB is correctly formatted + err = actualDel.ValidateBasic() + h.NoError(err) + // delegation is not activated by covenant yet + require.False(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + return actualDel +} diff --git a/x/btcstaking/keeper/msg_server.go b/x/btcstaking/keeper/msg_server.go new file mode 100644 index 000000000..9384e997f --- /dev/null +++ b/x/btcstaking/keeper/msg_server.go @@ -0,0 +1,657 @@ +package keeper + +import ( + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + + sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/btcstaking" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// UpdateParams updates the params +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) + } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := ms.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} + +// CreateFinalityProvider creates a finality provider +func (ms msgServer) CreateFinalityProvider(goCtx context.Context, req *types.MsgCreateFinalityProvider) (*types.MsgCreateFinalityProviderResponse, error) { + // ensure the finality provider address does not already exist + ctx := sdk.UnwrapSDKContext(goCtx) + // basic stateless checks + if err := req.ValidateBasic(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%v", err) + } + + // verify proof of possession + if err := req.Pop.Verify(req.BabylonPk, req.BtcPk, ms.btcNet); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid proof of possession: %v", err) + } + + // ensure commission rate is at least the minimum commission rate in parameters + if req.Commission.LT(ms.MinCommissionRate(ctx)) { + return nil, types.ErrCommissionLTMinRate.Wrapf("cannot set finality provider commission to less than minimum rate of %s", ms.MinCommissionRate(ctx)) + } + + if req.Commission.GT(sdkmath.LegacyOneDec()) { + return nil, types.ErrCommissionGTMaxRate + } + + // ensure finality provider does not already exist + if ms.HasFinalityProvider(ctx, *req.BtcPk) { + return nil, types.ErrFpRegistered + } + + // all good, add this finality provider + fp := types.FinalityProvider{ + Description: req.Description, + Commission: req.Commission, + BabylonPk: req.BabylonPk, + BtcPk: req.BtcPk, + Pop: req.Pop, + } + ms.SetFinalityProvider(ctx, &fp) + + // notify subscriber + if err := ctx.EventManager().EmitTypedEvent(&types.EventNewFinalityProvider{Fp: &fp}); err != nil { + return nil, err + } + + return &types.MsgCreateFinalityProviderResponse{}, nil +} + +// CreateBTCDelegation creates a BTC delegation +// TODO: refactor this handler. It's now too convoluted +func (ms msgServer) CreateBTCDelegation(goCtx context.Context, req *types.MsgCreateBTCDelegation) (*types.MsgCreateBTCDelegationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // basic stateless checks + if err := req.ValidateBasic(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%v", err) + } + + params := ms.GetParams(ctx) + btccParams := ms.btccKeeper.GetParams(ctx) + kValue, wValue := btccParams.BtcConfirmationDepth, btccParams.CheckpointFinalizationTimeout + + minUnbondingTime := types.MinimumUnbondingTime(params, btccParams) + + // Check unbonding time (staking time from unbonding tx) is larger than min unbonding time + // which is larger value from: + // - MinUnbondingTime + // - CheckpointFinalizationTimeout + if uint64(req.UnbondingTime) <= minUnbondingTime { + return nil, types.ErrInvalidUnbondingTx.Wrapf("unbonding time %d must be larger than %d", req.UnbondingTime, minUnbondingTime) + } + + // At this point we know that unbonding time in request: + // - is larger than min unbonding time + // - is smaller than math.MaxUint16 (due to check in req.ValidateBasic()) + validatedUnbondingTime := uint16(req.UnbondingTime) + + // verify proof of possession + if err := req.Pop.Verify(req.BabylonPk, req.BtcPk, ms.btcNet); err != nil { + return nil, types.ErrInvalidProofOfPossession.Wrapf("error while validating proof of posession: %v", err) + } + + // Ensure all finality providers are known to Babylon + for _, fpBTCPK := range req.FpBtcPkList { + if !ms.HasFinalityProvider(ctx, fpBTCPK) { + return nil, types.ErrFpNotFound.Wrapf("finality provider pk: %s", fpBTCPK.MarshalHex()) + } + } + + // Parse staking tx + stakingMsgTx, err := bbn.NewBTCTxFromBytes(req.StakingTx.Transaction) + if err != nil { + return nil, types.ErrInvalidStakingTx.Wrapf("cannot be parsed: %v", err) + } + + // Check staking tx is not duplicated + stakingTxHash := stakingMsgTx.TxHash() + delgation := ms.getBTCDelegation(ctx, stakingTxHash) + if delgation != nil { + return nil, types.ErrReusedStakingTx.Wrapf("duplicated tx hash: %s", stakingTxHash.String()) + } + + // Check if data provided in request, matches data to which staking tx is committed + fpPKs, err := bbn.NewBTCPKsFromBIP340PKs(req.FpBtcPkList) + if err != nil { + return nil, types.ErrInvalidStakingTx.Wrapf("cannot parse finality provider PK list: %v", err) + } + covenantPKs, err := bbn.NewBTCPKsFromBIP340PKs(params.CovenantPks) + if err != nil { + // programming error + panic("failed to parse covenant PKs in KVStore") + } + stakerPk := req.BtcPk.MustToBTCPK() + + stakingInfo, err := btcstaking.BuildStakingInfo( + stakerPk, + fpPKs, + covenantPKs, + params.CovenantQuorum, + uint16(req.StakingTime), + btcutil.Amount(req.StakingValue), + ms.btcNet, + ) + if err != nil { + return nil, types.ErrInvalidStakingTx.Wrapf("err: %v", err) + } + + stakingOutputIdx, err := bbn.GetOutputIdxInBTCTx(stakingMsgTx, stakingInfo.StakingOutput) + if err != nil { + return nil, types.ErrInvalidStakingTx.Wrap("staking tx does not contain expected staking output") + } + + // Check staking tx timelock has correct values + // get startheight and endheight of the timelock + stakingTxHeader := ms.btclcKeeper.GetHeaderByHash(ctx, req.StakingTx.Key.Hash) + if stakingTxHeader == nil { + return nil, fmt.Errorf("header that includes the staking tx is not found") + } + startHeight := stakingTxHeader.Height + endHeight := stakingTxHeader.Height + uint64(req.StakingTime) + + // ensure staking tx is k-deep + btcTip := ms.btclcKeeper.GetTipInfo(ctx) + stakingTxDepth := btcTip.Height - stakingTxHeader.Height + if stakingTxDepth < kValue { + return nil, types.ErrInvalidStakingTx.Wrapf("not k-deep: k=%d; depth=%d", kValue, stakingTxDepth) + } + // ensure staking tx's timelock has more than w BTC blocks left + if btcTip.Height+wValue >= endHeight { + return nil, types.ErrInvalidStakingTx.Wrapf("staking tx's timelock has no more than w(=%d) blocks left", wValue) + } + + // verify staking tx info, i.e., inclusion proof + if err := req.StakingTx.VerifyInclusion(stakingTxHeader.Header, ms.btccKeeper.GetPowLimit()); err != nil { + return nil, types.ErrInvalidStakingTx.Wrapf("not included in the Bitcoin chain: %v", err) + } + + // check slashing tx and its consistency with staking tx + slashingMsgTx, err := req.SlashingTx.ToMsgTx() + if err != nil { + return nil, types.ErrInvalidSlashingTx.Wrapf("cannot be converted to wire.MsgTx: %v", err) + } + + // decode slashing address + // TODO: Decode slashing address only once, as it is the same for all BTC delegations + slashingAddr, err := btcutil.DecodeAddress(params.SlashingAddress, ms.btcNet) + if err != nil { + panic(fmt.Errorf("failed to decode slashing address in genesis: %w", err)) + } + + // Check slashing tx and staking tx are valid and consistent + if err := btcstaking.CheckTransactions( + slashingMsgTx, + stakingMsgTx, + stakingOutputIdx, + params.MinSlashingTxFeeSat, + params.SlashingRate, + slashingAddr, + stakerPk, + validatedUnbondingTime, + ms.btcNet, + ); err != nil { + return nil, types.ErrInvalidStakingTx.Wrap(err.Error()) + } + + // verify delegator sig against slashing path of the staking tx's script + slashingSpendInfo, err := stakingInfo.SlashingPathSpendInfo() + if err != nil { + panic(fmt.Errorf("failed to construct slashing path from the staking tx: %w", err)) + } + + err = req.SlashingTx.VerifySignature( + stakingInfo.StakingOutput.PkScript, + stakingInfo.StakingOutput.Value, + slashingSpendInfo.GetPkScriptPath(), + stakerPk, + req.DelegatorSlashingSig, + ) + if err != nil { + return nil, types.ErrInvalidSlashingTx.Wrapf("invalid delegator signature: %v", err) + } + + // all good, construct BTCDelegation and insert BTC delegation + // NOTE: the BTC delegation does not have voting power yet. It will + // have voting power only when 1) its corresponding staking tx is k-deep, + // and 2) it receives a covenant signature + newBTCDel := &types.BTCDelegation{ + BabylonPk: req.BabylonPk, + BtcPk: req.BtcPk, + Pop: req.Pop, + FpBtcPkList: req.FpBtcPkList, + StartHeight: startHeight, + EndHeight: endHeight, + TotalSat: uint64(stakingInfo.StakingOutput.Value), + StakingTx: req.StakingTx.Transaction, + StakingOutputIdx: stakingOutputIdx, + SlashingTx: req.SlashingTx, + DelegatorSig: req.DelegatorSlashingSig, + UnbondingTime: uint32(validatedUnbondingTime), + CovenantSigs: nil, // NOTE: covenant signature will be submitted in a separate msg by covenant + BtcUndelegation: nil, // this will be constructed in below code + } + + /* + logics about early unbonding + */ + + // deserialize provided transactions + unbondingSlashingMsgTx, err := req.UnbondingSlashingTx.ToMsgTx() + if err != nil { + return nil, types.ErrInvalidSlashingTx.Wrapf("cannot convert unbonding slashing tx to wire.MsgTx: %v", err) + } + unbondingMsgTx, err := bbn.NewBTCTxFromBytes(req.UnbondingTx) + if err != nil { + return nil, types.ErrInvalidUnbondingTx.Wrapf("cannot be converted to wire.MsgTx: %v", err) + } + + // Check that unbonding tx input is pointing to staking tx + if !unbondingMsgTx.TxIn[0].PreviousOutPoint.Hash.IsEqual(&stakingTxHash) { + return nil, types.ErrInvalidUnbondingTx.Wrapf("slashing transaction must spend staking output") + } + // Check that staking tx output index matches unbonding tx output index + if unbondingMsgTx.TxIn[0].PreviousOutPoint.Index != stakingOutputIdx { + return nil, types.ErrInvalidUnbondingTx.Wrapf("slashing transaction input must spend staking output") + } + + // building unbonding info + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + newBTCDel.BtcPk.MustToBTCPK(), + fpPKs, + covenantPKs, + params.CovenantQuorum, + validatedUnbondingTime, + btcutil.Amount(req.UnbondingValue), + ms.btcNet, + ) + if err != nil { + return nil, types.ErrInvalidUnbondingTx.Wrapf("err: %v", err) + } + + // get unbonding output index + unbondingOutputIdx, err := bbn.GetOutputIdxInBTCTx(unbondingMsgTx, unbondingInfo.UnbondingOutput) + if err != nil { + return nil, types.ErrInvalidUnbondingTx.Wrapf("unbonding tx does not contain expected unbonding output") + } + + // Check that slashing tx and unbonding tx are valid and consistent + err = btcstaking.CheckTransactions( + unbondingSlashingMsgTx, + unbondingMsgTx, + unbondingOutputIdx, + params.MinSlashingTxFeeSat, + params.SlashingRate, + params.MustGetSlashingAddress(ms.btcNet), + stakerPk, + validatedUnbondingTime, + ms.btcNet, + ) + if err != nil { + return nil, types.ErrInvalidUnbondingTx.Wrapf("err: %v", err) + } + + // Check staker signature against slashing path of the unbonding tx + unbondingSlashingSpendInfo, err := unbondingInfo.SlashingPathSpendInfo() + if err != nil { + // our staking info was constructed by using BuildStakingInfo constructor, so if + // this fails, it is a programming error + panic(err) + } + + err = req.UnbondingSlashingTx.VerifySignature( + unbondingInfo.UnbondingOutput.PkScript, + unbondingInfo.UnbondingOutput.Value, + unbondingSlashingSpendInfo.GetPkScriptPath(), + newBTCDel.BtcPk.MustToBTCPK(), + req.DelegatorUnbondingSlashingSig, + ) + if err != nil { + return nil, types.ErrInvalidSlashingTx.Wrapf("invalid delegator signature: %v", err) + } + + // Check unbonding tx against staking tx. + // - that input points to the staking tx, staking output + // - fee is larger than 0 + if unbondingMsgTx.TxOut[0].Value >= stakingMsgTx.TxOut[newBTCDel.StakingOutputIdx].Value { + // Note: we do not enfore any minimum fee for unbonding tx, we only require that it is larger than 0 + // Given that unbonding tx must not be replacable and we do not allow sending it second time, it places + // burden on staker to choose right fee. + // Unbonding tx should not be replaceable at babylon level (and by extension on btc level), as this would + // allow staker to spam the network with unbonding txs, which would force covenant and finality provider to send signatures. + return nil, types.ErrInvalidUnbondingTx.Wrapf("unbonding tx fee must be larger that 0") + } + + // all good, add BTC undelegation + newBTCDel.BtcUndelegation = &types.BTCUndelegation{ + UnbondingTx: req.UnbondingTx, + SlashingTx: req.UnbondingSlashingTx, + DelegatorSlashingSig: req.DelegatorUnbondingSlashingSig, + DelegatorUnbondingSig: nil, + CovenantSlashingSigs: nil, + CovenantUnbondingSigList: nil, + } + + if err := ms.AddBTCDelegation(ctx, newBTCDel); err != nil { + panic(fmt.Errorf("failed to set BTC delegation that has passed verification: %w", err)) + } + + // notify subscriber + if err := ctx.EventManager().EmitTypedEvent(&types.EventNewBTCDelegation{BtcDel: newBTCDel}); err != nil { + panic(fmt.Errorf("failed to emit EventNewBTCDelegation: %w", err)) + } + + return &types.MsgCreateBTCDelegationResponse{}, nil +} + +// AddCovenantSig adds signatures from covenants to a BTC delegation +// TODO: refactor this handler. Now it's too convoluted +func (ms msgServer) AddCovenantSigs(goCtx context.Context, req *types.MsgAddCovenantSigs) (*types.MsgAddCovenantSigsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // basic stateless checks + if err := req.ValidateBasic(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%v", err) + } + + params := ms.GetParams(ctx) + + // ensure BTC delegation exists + btcDel, err := ms.GetBTCDelegation(ctx, req.StakingTxHash) + if err != nil { + return nil, err + } + + // ensure that the given covenant PK is in the parameter + if !params.HasCovenantPK(req.Pk) { + return nil, types.ErrInvalidCovenantPK.Wrapf("covenant pk: %s", req.Pk.MarshalHex()) + } + + if btcDel.IsSignedByCovMember(req.Pk) && btcDel.BtcUndelegation.IsSignedByCovMember(req.Pk) { + ms.Logger(ctx).Debug("Received duplicated covenant signature", "covenant pk", req.Pk.MarshalHex()) + return &types.MsgAddCovenantSigsResponse{}, nil + } + + if btcDel.HasCovenantQuorums(params.CovenantQuorum) { + ms.Logger(ctx).Debug("Received covenant signature after achieving quorum", "covenant pk", req.Pk.MarshalHex()) + return &types.MsgAddCovenantSigsResponse{}, nil + } + + // Note: we assume the order of adaptor sigs is matched to the + // order of finality providers in the delegation + // TODO: ensure the order for restaking, currently, we only have one finality provider + // one covenant emulator + if len(req.SlashingTxSigs) != len(btcDel.FpBtcPkList) { + return nil, types.ErrInvalidCovenantSig.Wrapf( + "number of covenant signatures: %d, number of finality providers being staked to: %d", + len(req.SlashingTxSigs), len(btcDel.FpBtcPkList)) + } + + /* + Verify each covenant adaptor signature over slashing tx + */ + stakingInfo, err := btcDel.GetStakingInfo(¶ms, ms.btcNet) + if err != nil { + panic(fmt.Errorf("failed to get staking info from a verified delegation: %w", err)) + } + slashingSpendInfo, err := stakingInfo.SlashingPathSpendInfo() + if err != nil { + // our staking info was constructed by using BuildStakingInfo constructor, so if + // this fails, it is a programming error + panic(err) + } + parsedSlashingAdaptorSignatures, err := btcDel.SlashingTx.ParseEncVerifyAdaptorSignatures( + stakingInfo.StakingOutput, + slashingSpendInfo, + req.Pk, + btcDel.FpBtcPkList, + req.SlashingTxSigs, + ) + if err != nil { + return nil, types.ErrInvalidCovenantSig.Wrapf("err: %v", err) + } + + // Check that the number of covenant sigs and number of the + // finality providers are matched + // Note: we assume the order of adaptor sigs is matched to the + // order of finality providers in the delegation + // TODO: ensure the order for restaking, currently, we only have one finality provider + // one covenant emulator + if len(req.SlashingUnbondingTxSigs) != len(btcDel.FpBtcPkList) { + return nil, types.ErrInvalidCovenantSig.Wrapf( + "number of covenant signatures: %d, number of finality providers being staked to: %d", + len(req.SlashingUnbondingTxSigs), len(btcDel.FpBtcPkList)) + } + + /* + Verify Schnorr signature over unbonding tx + */ + unbondingMsgTx, err := bbn.NewBTCTxFromBytes(btcDel.BtcUndelegation.UnbondingTx) + if err != nil { + panic(fmt.Errorf("failed to parse unbonding tx from existing delegation with hash %s : %v", req.StakingTxHash, err)) + } + unbondingSpendInfo, err := stakingInfo.UnbondingPathSpendInfo() + if err != nil { + // our staking info was constructed by using BuildStakingInfo constructor, so if + // this fails, it is a programming error + panic(err) + } + if err := btcstaking.VerifyTransactionSigWithOutputData( + unbondingMsgTx, + stakingInfo.StakingOutput.PkScript, + stakingInfo.StakingOutput.Value, + unbondingSpendInfo.GetPkScriptPath(), + req.Pk.MustToBTCPK(), + *req.UnbondingTxSig, + ); err != nil { + return nil, types.ErrInvalidCovenantSig.Wrap(err.Error()) + } + + /* + verify each adaptor signature on slashing unbonding tx + */ + unbondingOutput := unbondingMsgTx.TxOut[0] // unbonding tx always have only one output + unbondingInfo, err := btcDel.GetUnbondingInfo(¶ms, ms.btcNet) + if err != nil { + panic(err) + } + unbondingSlashingSpendInfo, err := unbondingInfo.SlashingPathSpendInfo() + if err != nil { + // our unbonding info was constructed by using BuildStakingInfo constructor, so if + // this fails, it is a programming error + panic(err) + } + parsedUnbondingSlashingAdaptorSignatures, err := btcDel.BtcUndelegation.SlashingTx.ParseEncVerifyAdaptorSignatures( + unbondingOutput, + unbondingSlashingSpendInfo, + req.Pk, + btcDel.FpBtcPkList, + req.SlashingUnbondingTxSigs, + ) + if err != nil { + return nil, types.ErrInvalidCovenantSig.Wrapf("err: %v", err) + } + + // All is fine add received signatures to the BTC delegation and BtcUndelegation + btcDel.AddCovenantSigs( + req.Pk, + parsedSlashingAdaptorSignatures, + req.UnbondingTxSig, + parsedUnbondingSlashingAdaptorSignatures, + ) + + ms.setBTCDelegation(ctx, btcDel) + + // notify subscriber + if err := ctx.EventManager().EmitTypedEvent(&types.EventActivateBTCDelegation{BtcDel: btcDel}); err != nil { + panic(fmt.Errorf("failed to emit EventActivateBTCDelegation: %w", err)) + } + + return &types.MsgAddCovenantSigsResponse{}, nil +} + +// BTCUndelegate adds a signature on the unbonding tx from the BTC delegator +// this effectively proves that the BTC delegator wants to unbond and Babylon +// will consider its BTC delegation unbonded +func (ms msgServer) BTCUndelegate(goCtx context.Context, req *types.MsgBTCUndelegate) (*types.MsgBTCUndelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // basic stateless checks + if err := req.ValidateBasic(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%v", err) + } + + bsParams := ms.GetParams(ctx) + + // ensure BTC delegation exists + btcDel, err := ms.GetBTCDelegation(ctx, req.StakingTxHash) + if err != nil { + return nil, err + } + + // ensure the BTC delegation with the given staking tx hash is active + btcTip := ms.btclcKeeper.GetTipInfo(ctx) + wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + if btcDel.GetStatus(btcTip.Height, wValue, bsParams.CovenantQuorum) != types.BTCDelegationStatus_ACTIVE { + return nil, types.ErrInvalidBTCUndelegateReq.Wrap("cannot unbond an inactive BTC delegation") + } + + // verify the signature on unbonding tx from delegator + unbondingMsgTx, err := bbn.NewBTCTxFromBytes(btcDel.BtcUndelegation.UnbondingTx) + if err != nil { + panic(fmt.Errorf("failed to parse unbonding tx from existing delegation with hash %s : %v", req.StakingTxHash, err)) + } + stakingInfo, err := btcDel.GetStakingInfo(&bsParams, ms.btcNet) + if err != nil { + panic(fmt.Errorf("failed to get staking info from a verified delegation: %w", err)) + } + unbondingSpendInfo, err := stakingInfo.UnbondingPathSpendInfo() + if err != nil { + // our staking info was constructed by using BuildStakingInfo constructor, so if + // this fails, it is a programming error + panic(err) + } + if err := btcstaking.VerifyTransactionSigWithOutputData( + unbondingMsgTx, + stakingInfo.StakingOutput.PkScript, + stakingInfo.StakingOutput.Value, + unbondingSpendInfo.GetPkScriptPath(), + btcDel.BtcPk.MustToBTCPK(), + *req.UnbondingTxSig, + ); err != nil { + return nil, types.ErrInvalidCovenantSig.Wrap(err.Error()) + } + + // all good, add the signature to BTC delegation's undelegation + // and set back + btcDel.BtcUndelegation.DelegatorUnbondingSig = req.UnbondingTxSig + ms.setBTCDelegation(ctx, btcDel) + + // notify subscriber about this unbonded BTC delegation + event := &types.EventUnbondedBTCDelegation{ + BtcPk: btcDel.BtcPk, + FpBtcPkList: btcDel.FpBtcPkList, + StakingTxHash: req.StakingTxHash, + UnbondingTxHash: unbondingMsgTx.TxHash().String(), + FromState: types.BTCDelegationStatus_ACTIVE, + } + if err := ctx.EventManager().EmitTypedEvent(event); err != nil { + panic(fmt.Errorf("failed to emit EventUnbondedBTCDelegation: %w", err)) + } + + return &types.MsgBTCUndelegateResponse{}, nil +} + +// SelectiveSlashingEvidence handles the evidence that a finality provider has +// selectively slashed a BTC delegation +func (ms msgServer) SelectiveSlashingEvidence(goCtx context.Context, req *types.MsgSelectiveSlashingEvidence) (*types.MsgSelectiveSlashingEvidenceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + bsParams := ms.GetParams(ctx) + + // ensure BTC delegation exists + btcDel, err := ms.GetBTCDelegation(ctx, req.StakingTxHash) + if err != nil { + return nil, err + } + // ensure the BTC delegation is active, or its BTC undelegation receives an + // unbonding signature from the staker + btcTip := ms.btclcKeeper.GetTipInfo(ctx) + wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + covQuorum := bsParams.CovenantQuorum + if btcDel.GetStatus(btcTip.Height, wValue, covQuorum) != types.BTCDelegationStatus_ACTIVE && !btcDel.IsUnbondedEarly() { + return nil, types.ErrBTCDelegationNotFound.Wrap("a BTC delegation that is not active or unbonding early cannot be slashed") + } + + // decode the finality provider's BTC SK/PK + fpSK, fpPK := btcec.PrivKeyFromBytes(req.RecoveredFpBtcSk) + fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(fpPK) + + // ensure the BTC delegation is staked to the given finality provider + fpIdx := btcDel.GetFpIdx(fpBTCPK) + if fpIdx == -1 { + return nil, types.ErrFpNotFound.Wrapf("BTC delegation is not staked to the finality provider") + } + + // ensure the finality provider exists and is not slashed + fp, err := ms.GetFinalityProvider(ctx, fpBTCPK.MustMarshal()) + if err != nil { + panic(types.ErrFpNotFound.Wrapf("failing to find the finality provider with BTC delegations")) + } + if fp.IsSlashed() { + return nil, types.ErrFpAlreadySlashed + } + + // at this point, the finality provider must have done selective slashing and must be + // adversarial + + // slash the finality provider now + if err := ms.SlashFinalityProvider(ctx, fpBTCPK.MustMarshal()); err != nil { + panic(err) // failed to slash the finality provider, must be programming error + } + + // emit selective slashing event + evidence := &types.SelectiveSlashingEvidence{ + StakingTxHash: req.StakingTxHash, + FpBtcPk: fpBTCPK, + RecoveredFpBtcSk: fpSK.Serialize(), + } + event := &types.EventSelectiveSlashing{Evidence: evidence} + if err := sdk.UnwrapSDKContext(ctx).EventManager().EmitTypedEvent(event); err != nil { + panic(fmt.Errorf("failed to emit EventSelectiveSlashing event: %w", err)) + } + + return &types.MsgSelectiveSlashingEvidenceResponse{}, nil +} diff --git a/x/btcstaking/keeper/msg_server_test.go b/x/btcstaking/keeper/msg_server_test.go new file mode 100644 index 000000000..053fcb7ca --- /dev/null +++ b/x/btcstaking/keeper/msg_server_test.go @@ -0,0 +1,672 @@ +package keeper_test + +import ( + "encoding/hex" + "errors" + "math" + "math/rand" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzMsgCreateFinalityProvider(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + h := NewHelper(t, nil, nil) + + // generate new finality providers + fps := []*types.FinalityProvider{} + for i := 0; i < int(datagen.RandomInt(r, 10)); i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + msg := &types.MsgCreateFinalityProvider{ + Signer: datagen.GenRandomAccount().Address, + Description: fp.Description, + Commission: fp.Commission, + BabylonPk: fp.BabylonPk, + BtcPk: fp.BtcPk, + Pop: fp.Pop, + } + _, err = h.MsgServer.CreateFinalityProvider(h.Ctx, msg) + require.NoError(t, err) + + fps = append(fps, fp) + } + // assert these finality providers exist in KVStore + for _, fp := range fps { + btcPK := *fp.BtcPk + require.True(t, h.BTCStakingKeeper.HasFinalityProvider(h.Ctx, btcPK)) + } + + // duplicated finality providers should not pass + for _, fp2 := range fps { + msg := &types.MsgCreateFinalityProvider{ + Signer: datagen.GenRandomAccount().Address, + Description: fp2.Description, + Commission: fp2.Commission, + BabylonPk: fp2.BabylonPk, + BtcPk: fp2.BtcPk, + Pop: fp2.Pop, + } + _, err := h.MsgServer.CreateFinalityProvider(h.Ctx, msg) + require.Error(t, err) + } + }) +} + +func FuzzCreateBTCDelegationAndAddCovenantSigs(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + require.NoError(t, err) + + // generate and insert new finality provider + _, fpPK, _ := h.CreateFinalityProvider(r) + + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, _, _, msgCreateBTCDel := h.CreateDelegation( + r, + fpPK, + changeAddress.EncodeAddress(), + stakingValue, + 1000, + ) + + // ensure consistency between the msg and the BTC delegation in DB + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.Equal(h.t, msgCreateBTCDel.BabylonPk, actualDel.BabylonPk) + require.Equal(h.t, msgCreateBTCDel.Pop, actualDel.Pop) + require.Equal(h.t, msgCreateBTCDel.StakingTx.Transaction, actualDel.StakingTx) + require.Equal(h.t, msgCreateBTCDel.SlashingTx, actualDel.SlashingTx) + // ensure the BTC delegation in DB is correctly formatted + err = actualDel.ValidateBasic() + h.NoError(err) + // delegation is not activated by covenant yet + require.False(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + + msgs := h.GenerateCovenantSignaturesMessages(r, covenantSKs, msgCreateBTCDel, actualDel) + + for _, msg := range msgs { + _, err = h.MsgServer.AddCovenantSigs(h.Ctx, msg) + h.NoError(err) + // check that submitting the same covenant signature does not produce an error + _, err = h.MsgServer.AddCovenantSigs(h.Ctx, msg) + h.NoError(err) + } + + // ensure the BTC delegation now has voting power + actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.True(h.t, actualDel.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + require.True(h.t, actualDel.BtcUndelegation.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum)) + votingPower := actualDel.VotingPower(h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height, h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout, h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum) + require.Equal(t, uint64(stakingValue), votingPower) + }) +} + +func FuzzBTCUndelegate(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + wValue := h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout + + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + require.NoError(t, err) + + // generate and insert new finality provider + _, fpPK, _ := h.CreateFinalityProvider(r) + + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, delSK, _, msgCreateBTCDel := h.CreateDelegation( + r, + fpPK, + changeAddress.EncodeAddress(), + stakingValue, + 1000, + ) + + // add covenant signatures to this BTC delegation + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, msgCreateBTCDel, actualDel) + + // ensure the BTC delegation is bonded right now + actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + btcTip := h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height + status := actualDel.GetStatus(btcTip, wValue, bsParams.CovenantQuorum) + require.Equal(t, types.BTCDelegationStatus_ACTIVE, status) + + // delegator wants to unbond + delUnbondingSig, err := actualDel.SignUnbondingTx(&bsParams, h.Net, delSK) + h.NoError(err) + _, err = h.MsgServer.BTCUndelegate(h.Ctx, &types.MsgBTCUndelegate{ + Signer: datagen.GenRandomAccount().Address, + StakingTxHash: stakingTxHash, + UnbondingTxSig: bbn.NewBIP340SignatureFromBTCSig(delUnbondingSig), + }) + h.NoError(err) + + // ensure the BTC delegation is unbonded + actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + status = actualDel.GetStatus(btcTip, wValue, bsParams.CovenantQuorum) + require.Equal(t, types.BTCDelegationStatus_UNBONDED, status) + }) +} + +func FuzzSelectiveSlashing(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + require.NoError(t, err) + + // generate and insert new finality provider + fpSK, fpPK, _ := h.CreateFinalityProvider(r) + fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPK) + + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, _, _, msgCreateBTCDel := h.CreateDelegation( + r, + fpPK, + changeAddress.EncodeAddress(), + stakingValue, + 1000, + ) + + // add covenant signatures to this BTC delegation + // so that the BTC delegation becomes bonded + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, msgCreateBTCDel, actualDel) + // now BTC delegation has all covenant signatures + actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.True(t, actualDel.HasCovenantQuorums(bsParams.CovenantQuorum)) + + // submit evidence of selective slashing + msg := &types.MsgSelectiveSlashingEvidence{ + Signer: datagen.GenRandomAccount().Address, + StakingTxHash: actualDel.MustGetStakingTxHash().String(), + RecoveredFpBtcSk: fpSK.Serialize(), + } + _, err = h.MsgServer.SelectiveSlashingEvidence(h.Ctx, msg) + h.NoError(err) + + // ensure the finality provider is slashed + slashedFp, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fpBtcPk.MustMarshal()) + h.NoError(err) + require.True(t, slashedFp.IsSlashed()) + }) +} + +func FuzzSelectiveSlashing_StakingTx(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + covenantSKs, _ := h.GenAndApplyParams(r) + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + require.NoError(t, err) + + // generate and insert new finality provider + fpSK, fpPK, _ := h.CreateFinalityProvider(r) + fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPK) + + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, _, _, msgCreateBTCDel := h.CreateDelegation( + r, + fpPK, + changeAddress.EncodeAddress(), + stakingValue, + 1000, + ) + + // add covenant signatures to this BTC delegation + // so that the BTC delegation becomes bonded + actualDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + h.CreateCovenantSigs(r, covenantSKs, msgCreateBTCDel, actualDel) + // now BTC delegation has all covenant signatures + actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + h.NoError(err) + require.True(t, actualDel.HasCovenantQuorums(bsParams.CovenantQuorum)) + + // finality provider pulls off selective slashing by decrypting covenant's adaptor signature + // on the slashing tx + // choose a random covenant adaptor signature + covIdx := datagen.RandomInt(r, int(bsParams.CovenantQuorum)) + covPK := bbn.NewBIP340PubKeyFromBTCPK(covenantSKs[covIdx].PubKey()) + fpIdx := datagen.RandomInt(r, len(actualDel.FpBtcPkList)) + covASig, err := actualDel.GetCovSlashingAdaptorSig(covPK, int(fpIdx), bsParams.CovenantQuorum) + h.NoError(err) + + // finality provider decrypts the covenant signature + decKey, err := asig.NewDecyptionKeyFromBTCSK(fpSK) + h.NoError(err) + decryptedCovenantSig := bbn.NewBIP340SignatureFromBTCSig(covASig.Decrypt(decKey)) + + // recover the fpSK by using adaptor signature and decrypted Schnorr signature + recoveredFPDecKey := covASig.Recover(decryptedCovenantSig.MustToBTCSig()) + recoveredFPSK := recoveredFPDecKey.ToBTCSK() + // ensure the recovered finality provider SK is same as the real one + require.Equal(t, fpSK.Serialize(), recoveredFPSK.Serialize()) + + // submit evidence of selective slashing + msg := &types.MsgSelectiveSlashingEvidence{ + Signer: datagen.GenRandomAccount().Address, + StakingTxHash: actualDel.MustGetStakingTxHash().String(), + RecoveredFpBtcSk: recoveredFPSK.Serialize(), + } + _, err = h.MsgServer.SelectiveSlashingEvidence(h.Ctx, msg) + h.NoError(err) + + // ensure the finality provider is slashed + slashedFp, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, fpBtcPk.MustMarshal()) + h.NoError(err) + require.True(t, slashedFp.IsSlashed()) + }) +} + +func TestDoNotAllowDelegationWithoutFinalityProvider(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set covenant PK to params + _, covenantPKs := h.GenAndApplyParams(r) + bsParams := h.BTCStakingKeeper.GetParams(h.Ctx) + bcParams := h.BTCCheckpointKeeper.GetParams(h.Ctx) + + minUnbondingTime := types.MinimumUnbondingTime( + bsParams, + bcParams, + ) + + slashingChangeLockTime := uint16(minUnbondingTime) + 1 + + // We only generate a finality provider, but not insert it into KVStore. So later + // insertion of delegation should fail. + _, fpPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + /* + generate and insert valid new BTC delegation + */ + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + stakingTimeBlocks := uint16(5) + stakingValue := int64(2 * 10e8) + testStakingInfo := datagen.GenBTCStakingSlashingInfo( + r, + t, + h.Net, + delSK, + []*btcec.PublicKey{fpPK}, + covenantPKs, + bsParams.CovenantQuorum, + stakingTimeBlocks, + stakingValue, + bsParams.SlashingAddress, + bsParams.SlashingRate, + slashingChangeLockTime, + ) + // get msgTx + stakingMsgTx := testStakingInfo.StakingTx + serializedStakingTx, err := bbn.SerializeBTCTx(stakingMsgTx) + require.NoError(t, err) + // random signer + signer := datagen.GenRandomAccount().Address + // random Babylon SK + delBabylonSK, delBabylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + // PoP + pop, err := types.NewPoP(delBabylonSK, delSK) + require.NoError(t, err) + // generate staking tx info + prevBlock, _ := datagen.GenRandomBtcdBlock(r, 0, nil) + btcHeaderWithProof := datagen.CreateBlockWithTransaction(r, &prevBlock.Header, stakingMsgTx) + btcHeader := btcHeaderWithProof.HeaderBytes + txInfo := btcctypes.NewTransactionInfo( + &btcctypes.TransactionKey{Index: 1, Hash: btcHeader.Hash()}, + serializedStakingTx, + btcHeaderWithProof.SpvProof.MerkleNodes, + ) + + slashingPathInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + // generate proper delegator sig + delegatorSig, err := testStakingInfo.SlashingTx.Sign( + stakingMsgTx, + 0, + slashingPathInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + stkTxHash := testStakingInfo.StakingTx.TxHash() + unbondingTime := 100 + 1 + unbondingValue := stakingValue - datagen.UnbondingTxFee // TODO: parameterise fee + testUnbondingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + t, + h.Net, + delSK, + []*btcec.PublicKey{fpPK}, + covenantPKs, + bsParams.CovenantQuorum, + wire.NewOutPoint(&stkTxHash, datagen.StakingOutIdx), + uint16(unbondingTime), + unbondingValue, + bsParams.SlashingAddress, + bsParams.SlashingRate, + slashingChangeLockTime, + ) + unbondingTx, err := bbn.SerializeBTCTx(testUnbondingInfo.UnbondingTx) + h.NoError(err) + delUnbondingSlashingSig, err := testUnbondingInfo.GenDelSlashingTxSig(delSK) + h.NoError(err) + + // all good, construct and send MsgCreateBTCDelegation message + msgCreateBTCDel := &types.MsgCreateBTCDelegation{ + Signer: signer, + BabylonPk: delBabylonPK.(*secp256k1.PubKey), + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(delSK.PubKey()), + Pop: pop, + StakingTime: uint32(stakingTimeBlocks), + StakingValue: stakingValue, + StakingTx: txInfo, + SlashingTx: testStakingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + UnbondingTx: unbondingTx, + UnbondingTime: uint32(unbondingTime), + UnbondingValue: unbondingValue, + UnbondingSlashingTx: testUnbondingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: delUnbondingSlashingSig, + } + _, err = h.MsgServer.CreateBTCDelegation(h.Ctx, msgCreateBTCDel) + require.Error(t, err) + require.True(t, errors.Is(err, types.ErrFpNotFound)) +} + +func TestCorrectUnbondingTimeInDelegation(t *testing.T) { + tests := []struct { + name string + finalizationTimeout uint64 + minUnbondingTime uint32 + unbondingTimeInDelegation uint16 + err error + }{ + { + name: "successful delegation when ubonding time in delegation is larger than finalization timeout when finalization timeout is larger than min unbonding time", + unbondingTimeInDelegation: 101, + minUnbondingTime: 99, + finalizationTimeout: 100, + err: nil, + }, + { + name: "failed delegation when ubonding time in delegation is not larger than finalization time when min unbonding time is lower than finalization timeout", + unbondingTimeInDelegation: 100, + minUnbondingTime: 99, + finalizationTimeout: 100, + err: types.ErrInvalidUnbondingTx, + }, + { + name: "successful delegation when ubonding time ubonding time in delegation is larger than min unbonding time when min unbonding time is larger than finalization timeout", + unbondingTimeInDelegation: 151, + minUnbondingTime: 150, + finalizationTimeout: 100, + err: nil, + }, + { + name: "failed delegation when ubonding time in delegation is not larger than minUnbondingTime when min unbonding time is larger than finalization timeout", + unbondingTimeInDelegation: 150, + minUnbondingTime: 150, + finalizationTimeout: 100, + err: types.ErrInvalidUnbondingTx, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + h := NewHelper(t, btclcKeeper, btccKeeper) + + // set all parameters + _, _ = h.GenAndApplyCustomParams(r, tt.finalizationTimeout, tt.minUnbondingTime) + + changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net) + require.NoError(t, err) + + // generate and insert new finality provider + _, fpPK, _ := h.CreateFinalityProvider(r) + + // generate and insert new BTC delegation + stakingValue := int64(2 * 10e8) + stakingTxHash, _, _, _, err := h.CreateDelegationCustom( + r, + fpPK, + changeAddress.EncodeAddress(), + stakingValue, + 1000, + tt.unbondingTimeInDelegation, + ) + if tt.err != nil { + require.Error(t, err) + require.True(t, errors.Is(err, tt.err)) + } else { + require.NoError(t, err) + // Retrieve delegation from keeper + delegation, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash) + require.NoError(t, err) + require.Equal(t, tt.unbondingTimeInDelegation, uint16(delegation.UnbondingTime)) + } + }) + } +} + +func createNDelegationsForFinalityProvider( + r *rand.Rand, + t *testing.T, + fpPK *btcec.PublicKey, + stakingValue int64, + numDelegations int, + quorum uint32, +) []*types.BTCDelegation { + var delegations []*types.BTCDelegation + for i := 0; i < numDelegations; i++ { + covenatnSks, _, err := datagen.GenRandomBTCKeyPairs(r, int(quorum)) + require.NoError(t, err) + + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + net := &chaincfg.SimNetParams + slashingAddress, err := datagen.GenRandomBTCAddress(r, net) + require.NoError(t, err) + + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + del, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + delSK, + covenatnSks, + quorum, + slashingAddress.EncodeAddress(), + 0, + 0+math.MaxUint16, + uint64(stakingValue), + slashingRate, + math.MaxUint16, + ) + require.NoError(t, err) + + delegations = append(delegations, del) + } + return delegations +} + +type ExpectedProviderData struct { + numDelegations int32 + stakingValue int32 +} + +func FuzzDeterminismBtcstakingBeginBlocker(f *testing.F) { + // less seeds than usual as this is pretty long running test + datagen.AddRandomSeedsToFuzzer(f, 5) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + valSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(1) + require.NoError(t, err) + + var expectedProviderData map[string]*ExpectedProviderData = make(map[string]*ExpectedProviderData) + + // Create two test apps from the same set of validators + h := testhelper.NewHelperWithValSet(t, valSet, privSigner) + h1 := testhelper.NewHelperWithValSet(t, valSet, privSigner) + + // Default params are the same in both apps + covQuorum := h.App.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum + maxFinalityProviders := int32(h.App.BTCStakingKeeper.GetParams(h.Ctx).MaxActiveFinalityProviders) + + // Number of finality providers from 10 to maxFinalityProviders + 10 + numFinalityProviders := int(r.Int31n(maxFinalityProviders) + 10) + + fps := datagen.CreateNFinalityProviders(r, t, numFinalityProviders) + + // Fill the databse of both apps with the same finality providers and delegations + for _, fp := range fps { + h.AddFinalityProvider(fp) + h1.AddFinalityProvider(fp) + } + + for _, fp := range fps { + // each finality provider has different amount of delegations with different amount + stakingValue := r.Int31n(200000) + 10000 + numDelegations := r.Int31n(10) + + if numDelegations > 0 { + expectedProviderData[fp.BtcPk.MarshalHex()] = &ExpectedProviderData{ + numDelegations: numDelegations, + stakingValue: stakingValue, + } + } + + delegations := createNDelegationsForFinalityProvider( + r, + t, + fp.BtcPk.MustToBTCPK(), + int64(stakingValue), + int(numDelegations), + covQuorum, + ) + + for _, del := range delegations { + h.AddDelegation(del) + h1.AddDelegation(del) + } + } + + // Execute block for both apps + ctx, err := h.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) + + ctx1, err := h1.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) + + // Given that there is no transactions and the data in db is the same + // app hash produced by both apps should be the same + appHash1 := hex.EncodeToString(ctx.BlockHeader().AppHash) + appHash2 := hex.EncodeToString(ctx1.BlockHeader().AppHash) + require.Equal(t, appHash1, appHash2) + }) +} diff --git a/x/btcstaking/keeper/params.go b/x/btcstaking/keeper/params.go new file mode 100644 index 000000000..4859efb28 --- /dev/null +++ b/x/btcstaking/keeper/params.go @@ -0,0 +1,36 @@ +package keeper + +import ( + "context" + "cosmossdk.io/math" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +// SetParams sets the x/btcstaking module parameters. +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { + if err := p.Validate(); err != nil { + return err + } + store := k.storeService.OpenKVStore(ctx) + bz := k.cdc.MustMarshal(&p) + return store.Set(types.ParamsKey, bz) +} + +// GetParams returns the current x/btcstaking module parameters. +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } + if bz == nil { + return p + } + k.cdc.MustUnmarshal(bz, &p) + return p +} + +// MinCommissionRate returns the minimal commission rate of finality providers +func (k Keeper) MinCommissionRate(ctx context.Context) math.LegacyDec { + return k.GetParams(ctx).MinCommissionRate +} diff --git a/x/btcstaking/keeper/params_test.go b/x/btcstaking/keeper/params_test.go new file mode 100644 index 000000000..adf55969b --- /dev/null +++ b/x/btcstaking/keeper/params_test.go @@ -0,0 +1,19 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/stretchr/testify/require" +) + +func TestGetParams(t *testing.T) { + k, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + params := types.DefaultParams() + + err := k.SetParams(ctx, params) + require.NoError(t, err) + + require.EqualValues(t, params, k.GetParams(ctx)) +} diff --git a/x/btcstaking/keeper/query.go b/x/btcstaking/keeper/query.go new file mode 100644 index 000000000..7a46601f8 --- /dev/null +++ b/x/btcstaking/keeper/query.go @@ -0,0 +1,7 @@ +package keeper + +import ( + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +var _ types.QueryServer = Keeper{} diff --git a/x/btcstaking/keeper/query_params.go b/x/btcstaking/keeper/query_params.go new file mode 100644 index 000000000..1dda6409c --- /dev/null +++ b/x/btcstaking/keeper/query_params.go @@ -0,0 +1,19 @@ +package keeper + +import ( + "context" + + "github.com/babylonchain/babylon/x/btcstaking/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil +} diff --git a/x/btcstaking/keeper/query_params_test.go b/x/btcstaking/keeper/query_params_test.go new file mode 100644 index 000000000..b4c07587b --- /dev/null +++ b/x/btcstaking/keeper/query_params_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/stretchr/testify/require" +) + +func TestParamsQuery(t *testing.T) { + keeper, ctx := testkeeper.BTCStakingKeeper(t, nil, nil) + params := types.DefaultParams() + + err := keeper.SetParams(ctx, params) + require.NoError(t, err) + + response, err := keeper.Params(ctx, &types.QueryParamsRequest{}) + require.NoError(t, err) + require.Equal(t, &types.QueryParamsResponse{Params: params}, response) +} diff --git a/x/btcstaking/keeper/voting_power_table.go b/x/btcstaking/keeper/voting_power_table.go new file mode 100644 index 000000000..c5bb04903 --- /dev/null +++ b/x/btcstaking/keeper/voting_power_table.go @@ -0,0 +1,153 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/runtime" + + "cosmossdk.io/store/prefix" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// IterateActiveFPs iterates over all finality providers that are not slashed +func (k Keeper) IterateActiveFPs(ctx context.Context, handler func(fp *types.FinalityProvider) bool) { + // filter out all finality providers with positive voting power + fpIter := k.finalityProviderStore(ctx).Iterator(nil, nil) + defer fpIter.Close() + for ; fpIter.Valid(); fpIter.Next() { + var fp types.FinalityProvider + k.cdc.MustUnmarshal(fpIter.Value(), &fp) + if fp.IsSlashed() { + // slashed finality provider is removed from finality provider set + continue + } + + shouldContinue := handler(&fp) + if !shouldContinue { + return + } + } +} + +// SetVotingPower sets the voting power of a given finality provider at a given Babylon height +func (k Keeper) SetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64, power uint64) { + store := k.votingPowerStore(ctx, height) + store.Set(fpBTCPK, sdk.Uint64ToBigEndian(power)) +} + +// GetVotingPower gets the voting power of a given finality provider at a given Babylon height +func (k Keeper) GetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64) uint64 { + if !k.HasFinalityProvider(ctx, fpBTCPK) { + return 0 + } + store := k.votingPowerStore(ctx, height) + powerBytes := store.Get(fpBTCPK) + if len(powerBytes) == 0 { + return 0 + } + return sdk.BigEndianToUint64(powerBytes) +} + +// GetCurrentVotingPower gets the voting power of a given finality provider at the current height +// NOTE: it's possible that the voting power table is 1 block behind CometBFT, e.g., when `BeginBlock` +// hasn't executed yet +func (k Keeper) GetCurrentVotingPower(ctx context.Context, fpBTCPK []byte) (uint64, uint64) { + // find the last recorded voting power table via iterator + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.VotingPowerKey) + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + + // no voting power table is known yet, return 0 + if !iter.Valid() { + return 0, 0 + } + + // there is known voting power table, find the last height + lastHeight := sdk.BigEndianToUint64(iter.Key()) + storeAtHeight := prefix.NewStore(store, sdk.Uint64ToBigEndian(lastHeight)) + + // if the finality provider is not known, return 0 voting power + if !k.HasFinalityProvider(ctx, fpBTCPK) { + return lastHeight, 0 + } + + // find the voting power of this finality provider + powerBytes := storeAtHeight.Get(fpBTCPK) + if len(powerBytes) == 0 { + return lastHeight, 0 + } + + return lastHeight, sdk.BigEndianToUint64(powerBytes) +} + +// HasVotingPowerTable checks if the voting power table exists at a given height +func (k Keeper) HasVotingPowerTable(ctx context.Context, height uint64) bool { + store := k.votingPowerStore(ctx, height) + iter := store.Iterator(nil, nil) + defer iter.Close() + return iter.Valid() +} + +// GetVotingPowerTable gets the voting power table, i.e., finality provider set at a given height +func (k Keeper) GetVotingPowerTable(ctx context.Context, height uint64) map[string]uint64 { + store := k.votingPowerStore(ctx, height) + iter := store.Iterator(nil, nil) + defer iter.Close() + + // if no finality provider at this height, return nil + if !iter.Valid() { + return nil + } + + // get all finality providers at this height + fpSet := map[string]uint64{} + for ; iter.Valid(); iter.Next() { + fpBTCPK, err := bbn.NewBIP340PubKey(iter.Key()) + if err != nil { + // failing to unmarshal finality provider BTC PK in KVStore is a programming error + panic(fmt.Errorf("%w: %w", bbn.ErrUnmarshal, err)) + } + fpSet[fpBTCPK.MarshalHex()] = sdk.BigEndianToUint64(iter.Value()) + } + + return fpSet +} + +// GetBTCStakingActivatedHeight returns the height when the BTC staking protocol is activated +// i.e., the first height where a finality provider has voting power +// Before the BTC staking protocol is activated, we don't index or tally any block +func (k Keeper) GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + votingPowerStore := prefix.NewStore(storeAdapter, types.VotingPowerKey) + iter := votingPowerStore.Iterator(nil, nil) + defer iter.Close() + // if the iterator is valid, then there exists a height that has a finality provider with voting power + if iter.Valid() { + return sdk.BigEndianToUint64(iter.Key()), nil + } else { + return 0, types.ErrBTCStakingNotActivated + } +} + +func (k Keeper) IsBTCStakingActivated(ctx context.Context) bool { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + votingPowerStore := prefix.NewStore(storeAdapter, types.VotingPowerKey) + iter := votingPowerStore.Iterator(nil, nil) + defer iter.Close() + // if the iterator is valid, then BTC staking is already activated + return iter.Valid() +} + +// votingPowerStore returns the KVStore of the finality providers' voting power +// prefix: (VotingPowerKey || Babylon block height) +// key: Bitcoin secp256k1 PK +// value: voting power quantified in Satoshi +func (k Keeper) votingPowerStore(ctx context.Context, height uint64) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + votingPowerStore := prefix.NewStore(storeAdapter, types.VotingPowerKey) + return prefix.NewStore(votingPowerStore, sdk.Uint64ToBigEndian(height)) +} diff --git a/x/btcstaking/keeper/voting_power_table_test.go b/x/btcstaking/keeper/voting_power_table_test.go new file mode 100644 index 000000000..6f6ae99ec --- /dev/null +++ b/x/btcstaking/keeper/voting_power_table_test.go @@ -0,0 +1,417 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzVotingPowerTable(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // generate a random batch of finality providers + fps := []*types.FinalityProvider{} + numFpsWithVotingPower := datagen.RandomInt(r, 10) + 2 + numFps := numFpsWithVotingPower + datagen.RandomInt(r, 10) + for i := uint64(0); i < numFps; i++ { + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + fps = append(fps, fp) + } + + // for the first numFpsWithVotingPower finality providers, generate a random number of BTC delegations + numBTCDels := datagen.RandomInt(r, 10) + 1 + stakingValue := datagen.RandomInt(r, 100000) + 100000 + for i := uint64(0); i < numFpsWithVotingPower; i++ { + for j := uint64(0); j < numBTCDels; j++ { + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fps[i].BtcPk}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, stakingValue, + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + } + } + + /* + Case 1: BTC height is 0 that is smaller than start height 1. + No finality privater will have voting power + */ + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 0}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + for i := uint64(0); i < numFps; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + require.Zero(t, power) + } + + /* + Case 2: move to 1st BTC block, then assert the first numFpsWithVotingPower finality providers have voting power + */ + babylonHeight += datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + for i := uint64(0); i < numFpsWithVotingPower; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + require.Equal(t, numBTCDels*stakingValue, power) + } + for i := numFpsWithVotingPower; i < numFps; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + require.Zero(t, power) + } + + // also, get voting power table and assert consistency + powerTable := keeper.GetVotingPowerTable(ctx, babylonHeight) + require.NotNil(t, powerTable) + for i := uint64(0); i < numFpsWithVotingPower; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + require.Equal(t, powerTable[fps[i].BtcPk.MarshalHex()], power) + } + // the activation height should be the current Babylon height as well + activatedHeight, err := keeper.GetBTCStakingActivatedHeight(ctx) + require.NoError(t, err) + require.Equal(t, babylonHeight, activatedHeight) + + /* + Case 3: slash a random finality provider and move on + then assert the slashed finality provider does not have voting power + */ + // slash a random finality provider + slashedIdx := datagen.RandomInt(r, int(numFpsWithVotingPower)) + slashedFp := fps[slashedIdx] + // This will be called to get the slashed height + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.SlashFinalityProvider(ctx, slashedFp.BtcPk.MustMarshal()) + require.NoError(t, err) + // move to later Babylon height and 2nd BTC height + babylonHeight += datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 2}).Times(1) + // index height and record power table + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + // check if the slashed finality provider's voting power becomes zero + for i := uint64(0); i < numFpsWithVotingPower; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + if i == slashedIdx { + require.Zero(t, power) + } else { + require.Equal(t, numBTCDels*stakingValue, power) + } + } + for i := numFpsWithVotingPower; i < numFps; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + require.Zero(t, power) + } + + // also, get voting power table and assert consistency + powerTable = keeper.GetVotingPowerTable(ctx, babylonHeight) + require.NotNil(t, powerTable) + for i := uint64(0); i < numFpsWithVotingPower; i++ { + power := keeper.GetVotingPower(ctx, *fps[i].BtcPk, babylonHeight) + if i == slashedIdx { + require.Zero(t, power) + } + require.Equal(t, powerTable[fps[i].BtcPk.MarshalHex()], power) + } + + /* + Case 4: move to 999th BTC block, then assert none of finality providers has voting power (since end height - w < BTC height) + */ + babylonHeight += datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 999}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + for _, fp := range fps { + power := keeper.GetVotingPower(ctx, *fp.BtcPk, babylonHeight) + require.Zero(t, power) + } + + // the activation height should be same as before + activatedHeight2, err := keeper.GetBTCStakingActivatedHeight(ctx) + require.NoError(t, err) + require.Equal(t, activatedHeight, activatedHeight2) + }) +} + +func FuzzVotingPowerTable_ActiveFinalityProviders(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // generate a random batch of finality providers, each with a BTC delegation with random power + fpsWithMeta := []*types.FinalityProviderWithMeta{} + numFps := datagen.RandomInt(r, 300) + 1 + for i := uint64(0); i < numFps; i++ { + // generate finality provider + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + + // delegate to this finality provider + stakingValue := datagen.RandomInt(r, 100000) + 100000 + fpBTCPK := fp.BtcPk + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fpBTCPK}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, stakingValue, // timelock period: 1-1000 + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + + // record voting power + fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ + BtcPk: fp.BtcPk, + VotingPower: stakingValue, + }) + } + + maxActiveFpsParam := keeper.GetParams(ctx).MaxActiveFinalityProviders + // get a map of expected active finality providers + expectedActiveFps := types.FilterTopNFinalityProviders(fpsWithMeta, maxActiveFpsParam) + expectedActiveFpsMap := map[string]uint64{} + for _, fp := range expectedActiveFps { + expectedActiveFpsMap[fp.BtcPk.MarshalHex()] = fp.VotingPower + } + + // record voting power table + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + // only finality providers in expectedActiveFpsMap have voting power + for _, fp := range fpsWithMeta { + power := keeper.GetVotingPower(ctx, fp.BtcPk.MustMarshal(), babylonHeight) + if expectedPower, ok := expectedActiveFpsMap[fp.BtcPk.MarshalHex()]; ok { + require.Equal(t, expectedPower, power) + } else { + require.Equal(t, uint64(0), power) + } + } + + // also, get voting power table and assert there is + // min(len(expectedActiveFps), MaxActiveFinalityProviders) active finality providers + powerTable := keeper.GetVotingPowerTable(ctx, babylonHeight) + expectedNumActiveFps := len(expectedActiveFpsMap) + if expectedNumActiveFps > int(maxActiveFpsParam) { + expectedNumActiveFps = int(maxActiveFpsParam) + } + require.Len(t, powerTable, expectedNumActiveFps) + // assert consistency of voting power + for pkHex, expectedPower := range expectedActiveFpsMap { + require.Equal(t, powerTable[pkHex], expectedPower) + } + }) +} + +func FuzzVotingPowerTable_ActiveFinalityProviderRotation(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock BTC light client and BTC checkpoint modules + btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl) + btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl) + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() + keeper, ctx := keepertest.BTCStakingKeeper(t, btclcKeeper, btccKeeper) + + // covenant and slashing addr + covenantSKs, _, covenantQuorum := datagen.GenCovenantCommittee(r) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // generate a random batch of finality providers, each with a BTC delegation with random power + fpsWithMeta := []*types.FinalityProviderWithMeta{} + numFps := uint64(200) // there has to be more than `maxActiveFinalityProviders` finality providers + for i := uint64(0); i < numFps; i++ { + // generate finality provider + fp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) + keeper.SetFinalityProvider(ctx, fp) + + // delegate to this finality provider + stakingValue := datagen.RandomInt(r, 100000) + 100000 + fpBTCPK := fp.BtcPk + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*fpBTCPK}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, stakingValue, // timelock period: 1-1000 + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + + // record voting power + fpsWithMeta = append(fpsWithMeta, &types.FinalityProviderWithMeta{ + BtcPk: fp.BtcPk, + VotingPower: stakingValue, + }) + } + + // record voting power table + babylonHeight := datagen.RandomInt(r, 10) + 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + // get maps of active/inactive finality providers + activeFpsMap := map[string]uint64{} + inactiveFpsMap := map[string]uint64{} + for _, fp := range fpsWithMeta { + power := keeper.GetVotingPower(ctx, fp.BtcPk.MustMarshal(), babylonHeight) + if power > 0 { + activeFpsMap[fp.BtcPk.MarshalHex()] = power + } else { + inactiveFpsMap[fp.BtcPk.MarshalHex()] = power + } + } + + // delegate a huge amount of tokens to one of the inactive finality provider + var activatedFpBTCPK *bbn.BIP340PubKey + for fpBTCPKHex := range inactiveFpsMap { + stakingValue := uint64(10000000) + activatedFpBTCPK, _ = bbn.NewBIP340PubKeyFromHex(fpBTCPKHex) + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + []bbn.BIP340PubKey{*activatedFpBTCPK}, + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1, 1000, stakingValue, // timelock period: 1-1000 + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + err = keeper.AddBTCDelegation(ctx, btcDel) + require.NoError(t, err) + + break + } + + // record voting power table + babylonHeight += 1 + ctx = datagen.WithCtxHeight(ctx, babylonHeight) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(&btclctypes.BTCHeaderInfo{Height: 1}).Times(1) + err = keeper.BeginBlocker(ctx) + require.NoError(t, err) + + // ensure that the activated finality provider now has entered the active finality provider set + // i.e., has voting power + power := keeper.GetVotingPower(ctx, activatedFpBTCPK.MustMarshal(), babylonHeight) + require.Positive(t, power) + }) +} diff --git a/x/btcstaking/module.go b/x/btcstaking/module.go new file mode 100644 index 000000000..a415eee5a --- /dev/null +++ b/x/btcstaking/module.go @@ -0,0 +1,150 @@ +package btcstaking + +import ( + "context" + "cosmossdk.io/core/appmodule" + "encoding/json" + "fmt" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/babylonchain/babylon/x/btcstaking/client/cli" + "github.com/babylonchain/babylon/x/btcstaking/keeper" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck // generally we don't handle errors here +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted) +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) +} + +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { + return EndBlocker(ctx, am.keeper) +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker +} diff --git a/x/btcstaking/types/btc_delegation.go b/x/btcstaking/types/btc_delegation.go new file mode 100644 index 000000000..c2b1f5833 --- /dev/null +++ b/x/btcstaking/types/btc_delegation.go @@ -0,0 +1,433 @@ +package types + +import ( + "bytes" + "fmt" + math "math" + + "github.com/babylonchain/babylon/btcstaking" + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" +) + +func NewBTCDelegationStatusFromString(statusStr string) (BTCDelegationStatus, error) { + switch statusStr { + case "pending": + return BTCDelegationStatus_PENDING, nil + case "active": + return BTCDelegationStatus_ACTIVE, nil + case "unbonded": + return BTCDelegationStatus_UNBONDED, nil + case "any": + return BTCDelegationStatus_ANY, nil + default: + return -1, fmt.Errorf("invalid status string; should be one of {pending, active, unbonding, unbonded, any}") + } +} + +func (d *BTCDelegation) GetStakingTime() uint16 { + diff := d.EndHeight - d.StartHeight + + if diff > math.MaxUint16 { + // In valid delegation, EndHeight is always greater than StartHeight and it is always uint16 value + panic("invalid delegation in database") + } + + return uint16(diff) +} + +// GetFpIdx returns the index of the finality provider in the list of finality providers +// that the BTC delegation is restaked to +func (d *BTCDelegation) GetFpIdx(fpBTCPK *bbn.BIP340PubKey) int { + for i := 0; i < len(d.FpBtcPkList); i++ { + if d.FpBtcPkList[i].Equals(fpBTCPK) { + return i + } + } + return -1 +} + +func (d *BTCDelegation) GetCovSlashingAdaptorSig( + covBTCPK *bbn.BIP340PubKey, + valIdx int, + quorum uint32, +) (*asig.AdaptorSignature, error) { + if !d.HasCovenantQuorums(quorum) { + return nil, ErrInvalidDelegationState.Wrap("BTC delegation does not have a covenant quorum yet") + } + for _, covASigs := range d.CovenantSigs { + if covASigs.CovPk.Equals(covBTCPK) { + if valIdx >= len(covASigs.AdaptorSigs) { + return nil, ErrFpNotFound.Wrap("validator index is out of scope") + } + sigBytes := covASigs.AdaptorSigs[valIdx] + return asig.NewAdaptorSignatureFromBytes(sigBytes) + } + } + + return nil, ErrInvalidCovenantPK.Wrap("covenant PK is not found") +} + +// IsUnbondedEarly returns whether the delegator has signed unbonding signature. +// Signing unbonding signature means the delegator wants to unbond early, and +// Babylon will consider this BTC delegation unbonded directly +func (d *BTCDelegation) IsUnbondedEarly() bool { + return d.BtcUndelegation.DelegatorUnbondingSig != nil +} + +// GetStatus returns the status of the BTC Delegation based on BTC height, w value, and covenant quorum +// Pending: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation does not have covenant signatures +// Active: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation has quorum number of signatures over slashing tx, unbonding tx, and slashing unbonding tx from covenant committee +// Unbonded: the BTC height is larger than `endHeight-w` or the BTC delegation has received a signature on unbonding tx from the delegator +func (d *BTCDelegation) GetStatus(btcHeight uint64, w uint64, covenantQuorum uint32) BTCDelegationStatus { + if d.IsUnbondedEarly() { + return BTCDelegationStatus_UNBONDED + } + + if btcHeight < d.StartHeight || btcHeight+w > d.EndHeight { + // staking tx's timelock has not begun, or is less than w BTC + // blocks left, or is expired + return BTCDelegationStatus_UNBONDED + } + + // at this point, BTC delegation has an active timelock, and Babylon is not + // aware of unbonding tx with delegator's signature + if d.HasCovenantQuorums(covenantQuorum) { + // this BTC delegation receives covenant quorums on + // {slashing/unbonding/unbondingslashing} txs, thus is active + return BTCDelegationStatus_ACTIVE + } + + // no covenant quorum yet, pending + return BTCDelegationStatus_PENDING +} + +// VotingPower returns the voting power of the BTC delegation at a given BTC height +// and a given w value. +// The BTC delegation d has voting power iff it is active. +func (d *BTCDelegation) VotingPower(btcHeight uint64, w uint64, covenantQuorum uint32) uint64 { + if d.GetStatus(btcHeight, w, covenantQuorum) != BTCDelegationStatus_ACTIVE { + return 0 + } + return d.GetTotalSat() +} + +func (d *BTCDelegation) GetStakingTxHash() (chainhash.Hash, error) { + parsed, err := bbn.NewBTCTxFromBytes(d.StakingTx) + + if err != nil { + return chainhash.Hash{}, err + } + + return parsed.TxHash(), nil +} + +func (d *BTCDelegation) MustGetStakingTxHash() chainhash.Hash { + txHash, err := d.GetStakingTxHash() + + if err != nil { + panic(err) + } + + return txHash +} + +func (d *BTCDelegation) ValidateBasic() error { + if d.BabylonPk == nil { + return fmt.Errorf("empty Babylon public key") + } + if d.BtcPk == nil { + return fmt.Errorf("empty BTC public key") + } + if d.Pop == nil { + return fmt.Errorf("empty proof of possession") + } + if len(d.FpBtcPkList) == 0 { + return fmt.Errorf("empty list of finality provider PKs") + } + if ExistsDup(d.FpBtcPkList) { + return fmt.Errorf("list of finality provider PKs has duplication") + } + if d.StakingTx == nil { + return fmt.Errorf("empty staking tx") + } + if d.SlashingTx == nil { + return fmt.Errorf("empty slashing tx") + } + if d.DelegatorSig == nil { + return fmt.Errorf("empty delegator signature") + } + + // ensure staking tx is correctly formatted + if _, err := bbn.NewBTCTxFromBytes(d.StakingTx); err != nil { + return err + } + if err := d.Pop.ValidateBasic(); err != nil { + return err + } + + return nil +} + +// HasCovenantQuorum returns whether a BTC delegation has a quorum number of signatures +// from covenant members, including +// - adaptor signatures on slashing tx +// - Schnorr signatures on unbonding tx +// - adaptor signatrues on unbonding slashing tx +func (d *BTCDelegation) HasCovenantQuorums(quorum uint32) bool { + return uint32(len(d.CovenantSigs)) >= quorum && d.BtcUndelegation.HasCovenantQuorums(quorum) +} + +// IsSignedByCovMember checks whether the given covenant PK has signed the delegation +func (d *BTCDelegation) IsSignedByCovMember(covPk *bbn.BIP340PubKey) bool { + for _, sigInfo := range d.CovenantSigs { + if covPk.Equals(sigInfo.CovPk) { + return true + } + } + + return false +} + +// AddCovenantSigs adds signatures on the slashing tx from the given +// covenant, where each signature is an adaptor signature encrypted by +// each finality provider's PK this BTC delegation restakes to +// It is up to the caller to ensure that given adaptor signatures are valid or +// that they were not added before +func (d *BTCDelegation) AddCovenantSigs( + covPk *bbn.BIP340PubKey, + stakingSlashingSigs []asig.AdaptorSignature, + unbondingSig *bbn.BIP340Signature, + unbondingSlashingSigs []asig.AdaptorSignature, +) { + adaptorSigs := make([][]byte, 0, len(stakingSlashingSigs)) + for _, s := range stakingSlashingSigs { + adaptorSigs = append(adaptorSigs, s.MustMarshal()) + } + covSigs := &CovenantAdaptorSignatures{CovPk: covPk, AdaptorSigs: adaptorSigs} + + d.CovenantSigs = append(d.CovenantSigs, covSigs) + // add unbonding sig and unbonding slashing adaptor sig + d.BtcUndelegation.addCovenantSigs(covPk, unbondingSig, unbondingSlashingSigs) +} + +// GetStakingInfo returns the staking info of the BTC delegation +// the staking info can be used for constructing witness of slashing tx +// with access to a finality provider's SK +func (d *BTCDelegation) GetStakingInfo(bsParams *Params, btcNet *chaincfg.Params) (*btcstaking.StakingInfo, error) { + fpBtcPkList, err := bbn.NewBTCPKsFromBIP340PKs(d.FpBtcPkList) + if err != nil { + return nil, fmt.Errorf("failed to convert finality provider pks to BTC pks %v", err) + } + covenantBtcPkList, err := bbn.NewBTCPKsFromBIP340PKs(bsParams.CovenantPks) + if err != nil { + return nil, fmt.Errorf("failed to convert covenant pks to BTC pks %v", err) + } + stakingInfo, err := btcstaking.BuildStakingInfo( + d.BtcPk.MustToBTCPK(), + fpBtcPkList, + covenantBtcPkList, + bsParams.CovenantQuorum, + d.GetStakingTime(), + btcutil.Amount(d.TotalSat), + btcNet, + ) + if err != nil { + return nil, fmt.Errorf("could not create BTC staking info: %v", err) + } + return stakingInfo, nil +} + +func (d *BTCDelegation) SignUnbondingTx(bsParams *Params, btcNet *chaincfg.Params, sk *btcec.PrivateKey) (*schnorr.Signature, error) { + stakingTx, err := bbn.NewBTCTxFromBytes(d.StakingTx) + if err != nil { + return nil, fmt.Errorf("failed to parse staking transaction: %v", err) + } + unbondingTx, err := bbn.NewBTCTxFromBytes(d.BtcUndelegation.UnbondingTx) + if err != nil { + return nil, fmt.Errorf("failed to parse unbonding transaction: %v", err) + } + stakingInfo, err := d.GetStakingInfo(bsParams, btcNet) + if err != nil { + return nil, err + } + unbondingPath, err := stakingInfo.UnbondingPathSpendInfo() + if err != nil { + return nil, err + } + + sig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingTx, + stakingTx, + d.StakingOutputIdx, + unbondingPath.GetPkScriptPath(), + sk, + ) + if err != nil { + return nil, err + } + return sig, nil +} + +// GetUnbondingInfo returns the unbonding info of the BTC delegation +// the unbonding info can be used for constructing witness of unbonding slashing +// tx with access to a finality provider's SK +func (d *BTCDelegation) GetUnbondingInfo(bsParams *Params, btcNet *chaincfg.Params) (*btcstaking.UnbondingInfo, error) { + fpBtcPkList, err := bbn.NewBTCPKsFromBIP340PKs(d.FpBtcPkList) + if err != nil { + return nil, fmt.Errorf("failed to convert finality provider pks to BTC pks: %v", err) + } + + covenantBtcPkList, err := bbn.NewBTCPKsFromBIP340PKs(bsParams.CovenantPks) + if err != nil { + return nil, fmt.Errorf("failed to convert covenant pks to BTC pks: %v", err) + } + unbondingTx, err := bbn.NewBTCTxFromBytes(d.BtcUndelegation.UnbondingTx) + if err != nil { + return nil, fmt.Errorf("failed to parse unbonding transaction: %v", err) + } + + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + d.BtcPk.MustToBTCPK(), + fpBtcPkList, + covenantBtcPkList, + bsParams.CovenantQuorum, + uint16(d.GetUnbondingTime()), + btcutil.Amount(unbondingTx.TxOut[0].Value), + btcNet, + ) + if err != nil { + return nil, fmt.Errorf("could not create BTC staking info: %v", err) + } + + return unbondingInfo, nil +} + +func (d *BTCDelegation) BuildSlashingTxWithWitness(bsParams *Params, btcNet *chaincfg.Params, fpSK *btcec.PrivateKey) (*wire.MsgTx, error) { + stakingMsgTx, err := bbn.NewBTCTxFromBytes(d.StakingTx) + if err != nil { + return nil, fmt.Errorf("failed to convert a Babylon staking tx to wire.MsgTx: %w", err) + } + + // get staking info + stakingInfo, err := d.GetStakingInfo(bsParams, btcNet) + if err != nil { + return nil, fmt.Errorf("could not create BTC staking info: %v", err) + } + slashingSpendInfo, err := stakingInfo.SlashingPathSpendInfo() + if err != nil { + return nil, fmt.Errorf("could not get slashing spend info: %v", err) + } + + // TODO: work with restaking + covAdaptorSigs, err := GetOrderedCovenantSignatures(0, d.CovenantSigs, bsParams) + if err != nil { + return nil, fmt.Errorf("failed to get ordered covenant adaptor signatures: %w", err) + } + + // assemble witness for slashing tx + slashingMsgTxWithWitness, err := d.SlashingTx.BuildSlashingTxWithWitness( + fpSK, + stakingMsgTx, + d.StakingOutputIdx, + d.DelegatorSig, + covAdaptorSigs, + slashingSpendInfo, + ) + if err != nil { + return nil, fmt.Errorf( + "failed to build witness for BTC delegation of %s under finality provider %s: %v", + d.BtcPk.MarshalHex(), + bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()).MarshalHex(), + err, + ) + } + + return slashingMsgTxWithWitness, nil +} + +func (d *BTCDelegation) BuildUnbondingSlashingTxWithWitness(bsParams *Params, btcNet *chaincfg.Params, fpSK *btcec.PrivateKey) (*wire.MsgTx, error) { + unbondingMsgTx, err := bbn.NewBTCTxFromBytes(d.BtcUndelegation.UnbondingTx) + if err != nil { + return nil, fmt.Errorf("failed to convert a Babylon unbonding tx to wire.MsgTx: %w", err) + } + + // get unbonding info + unbondingInfo, err := d.GetUnbondingInfo(bsParams, btcNet) + if err != nil { + return nil, fmt.Errorf("could not create BTC unbonding info: %v", err) + } + slashingSpendInfo, err := unbondingInfo.SlashingPathSpendInfo() + if err != nil { + return nil, fmt.Errorf("could not get unbonding slashing spend info: %v", err) + } + + // TODO: work with restaking + covAdaptorSigs, err := GetOrderedCovenantSignatures(0, d.BtcUndelegation.CovenantSlashingSigs, bsParams) + if err != nil { + return nil, fmt.Errorf("failed to get ordered covenant adaptor signatures: %w", err) + } + + // assemble witness for unbonding slashing tx + slashingMsgTxWithWitness, err := d.BtcUndelegation.SlashingTx.BuildSlashingTxWithWitness( + fpSK, + unbondingMsgTx, + 0, + d.BtcUndelegation.DelegatorSlashingSig, + covAdaptorSigs, + slashingSpendInfo, + ) + if err != nil { + return nil, fmt.Errorf( + "failed to build witness for unbonding BTC delegation %s under finality provider %s: %v", + d.BtcPk.MarshalHex(), + bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()).MarshalHex(), + err, + ) + } + + return slashingMsgTxWithWitness, nil +} + +func NewBTCDelegatorDelegationIndex() *BTCDelegatorDelegationIndex { + return &BTCDelegatorDelegationIndex{ + StakingTxHashList: [][]byte{}, + } +} + +func (i *BTCDelegatorDelegationIndex) Has(stakingTxHash chainhash.Hash) bool { + for _, hash := range i.StakingTxHashList { + if bytes.Equal(stakingTxHash[:], hash) { + return true + } + } + return false +} + +func (i *BTCDelegatorDelegationIndex) Add(stakingTxHash chainhash.Hash) error { + // ensure staking tx hash is not duplicated + for _, hash := range i.StakingTxHashList { + if bytes.Equal(stakingTxHash[:], hash) { + return fmt.Errorf("the given stakingTxHash %s is duplicated", stakingTxHash.String()) + } + } + // add + i.StakingTxHashList = append(i.StakingTxHashList, stakingTxHash[:]) + + return nil +} + +// VotingPower calculates the total voting power of all BTC delegations +func (dels *BTCDelegatorDelegations) VotingPower(btcHeight uint64, w uint64, covenantQuorum uint32) uint64 { + power := uint64(0) + for _, del := range dels.Dels { + power += del.VotingPower(btcHeight, w, covenantQuorum) + } + return power +} diff --git a/x/btcstaking/types/btc_delegation_test.go b/x/btcstaking/types/btc_delegation_test.go new file mode 100644 index 000000000..92ee43742 --- /dev/null +++ b/x/btcstaking/types/btc_delegation_test.go @@ -0,0 +1,162 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" + + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + btctest "github.com/babylonchain/babylon/testutil/bitcoin" + "github.com/babylonchain/babylon/testutil/datagen" + "github.com/babylonchain/babylon/x/btcstaking/types" +) + +func FuzzBTCDelegation(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 100) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + btcDel := &types.BTCDelegation{} + // randomise voting power + btcDel.TotalSat = datagen.RandomInt(r, 100000) + btcDel.BtcUndelegation = &types.BTCUndelegation{} + + // randomise covenant sig + hasCovenantSig := datagen.RandomInt(r, 2) == 0 + if hasCovenantSig { + encKey, _, err := asig.GenKeyPair() + require.NoError(t, err) + covenantSK, _ := btcec.PrivKeyFromBytes( + []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + ) + covenantSig, err := asig.EncSign(covenantSK, encKey, datagen.GenRandomByteArray(r, 32)) + require.NoError(t, err) + covPk, err := datagen.GenRandomBIP340PubKey(r) + require.NoError(t, err) + covSigInfo := &types.CovenantAdaptorSignatures{ + CovPk: covPk, + AdaptorSigs: [][]byte{covenantSig.MustMarshal()}, + } + btcDel.CovenantSigs = []*types.CovenantAdaptorSignatures{covSigInfo} + btcDel.BtcUndelegation.CovenantSlashingSigs = btcDel.CovenantSigs // doesn't matter + btcDel.BtcUndelegation.CovenantUnbondingSigList = []*types.SignatureInfo{&types.SignatureInfo{}} // doesn't matter + } + + // randomise start height and end height + btcDel.StartHeight = datagen.RandomInt(r, 100) + btcDel.EndHeight = btcDel.StartHeight + datagen.RandomInt(r, 100) + + // randomise BTC tip and w + btcHeight := btcDel.StartHeight + datagen.RandomInt(r, 50) + w := datagen.RandomInt(r, 50) + + // test expected voting power + hasVotingPower := hasCovenantSig && btcDel.StartHeight <= btcHeight && btcHeight+w <= btcDel.EndHeight + actualVotingPower := btcDel.VotingPower(btcHeight, w, 1) + if hasVotingPower { + require.Equal(t, btcDel.TotalSat, actualVotingPower) + } else { + require.Equal(t, uint64(0), actualVotingPower) + } + }) +} + +func FuzzBTCDelegation_SlashingTx(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + net := &chaincfg.SimNetParams + + delSK, delPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + delBTCPK := bbn.NewBIP340PubKeyFromBTCPK(delPK) + + fpSK, fpPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + fpPKList := []*btcec.PublicKey{fpPK} + + // (3, 5) covenant committee + covenantSKs, covenantPKs, err := datagen.GenRandomBTCKeyPairs(r, 5) + require.NoError(t, err) + covenantQuorum := uint32(3) + + stakingTimeBlocks := uint16(5) + stakingValue := int64(2 * 10e8) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + + slashingChangeLockTime := uint16(101) + + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + testInfo := datagen.GenBTCStakingSlashingInfo( + r, + t, + net, + delSK, + fpPKList, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + stakingValue, + slashingAddress.EncodeAddress(), + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + + stakingTxBytes, err := bbn.SerializeBTCTx(testInfo.StakingTx) + require.NoError(t, err) + + // spend info of the slashing tx + slashingSpendInfo, err := testInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // delegator signs the slashing tx + delSig, err := testInfo.SlashingTx.Sign(testInfo.StakingTx, 0, slashingSpendInfo.GetPkScriptPath(), delSK) + require.NoError(t, err) + // covenant signs (using adaptor signature) the slashing tx + covenantSigs, err := datagen.GenCovenantAdaptorSigs(covenantSKs, []*btcec.PublicKey{fpPK}, testInfo.StakingTx, slashingSpendInfo.GetPkScriptPath(), testInfo.SlashingTx) + require.NoError(t, err) + covenantSigs = covenantSigs[2:] // discard 2 out of 5 signatures + + // construct the BTC delegation with everything + btcDel := &types.BTCDelegation{ + BabylonPk: nil, // not relevant here + BtcPk: delBTCPK, + Pop: nil, // not relevant here + FpBtcPkList: bbn.NewBIP340PKsFromBTCPKs(fpPKList), + StartHeight: 1000, // not relevant here + EndHeight: uint64(1000 + stakingTimeBlocks), + TotalSat: uint64(stakingValue), + StakingTx: stakingTxBytes, + StakingOutputIdx: 0, + SlashingTx: testInfo.SlashingTx, + DelegatorSig: delSig, + CovenantSigs: covenantSigs, + } + + bsParams := &types.Params{ + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: covenantQuorum, + } + btcNet := &chaincfg.SimNetParams + + // build slashing tx with witness for spending the staking tx + slashingTxWithWitness, err := btcDel.BuildSlashingTxWithWitness(bsParams, btcNet, fpSK) + require.NoError(t, err) + + // assert execution + btctest.AssertSlashingTxExecution(t, testInfo.StakingInfo.StakingOutput, slashingTxWithWitness) + }) +} diff --git a/x/btcstaking/types/btc_slashing_tx.go b/x/btcstaking/types/btc_slashing_tx.go new file mode 100644 index 000000000..afb1da6e9 --- /dev/null +++ b/x/btcstaking/types/btc_slashing_tx.go @@ -0,0 +1,302 @@ +package types + +import ( + "bytes" + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + + "github.com/babylonchain/babylon/btcstaking" + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" +) + +type BTCSlashingTx []byte + +func NewBTCSlashingTxFromMsgTx(msgTx *wire.MsgTx) (*BTCSlashingTx, error) { + var buf bytes.Buffer + err := msgTx.Serialize(&buf) + if err != nil { + return nil, err + } + + tx := BTCSlashingTx(buf.Bytes()) + return &tx, nil +} + +func NewBTCSlashingTxFromHex(txHex string) (*BTCSlashingTx, error) { + txBytes, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + var tx BTCSlashingTx + if err := tx.Unmarshal(txBytes); err != nil { + return nil, err + } + return &tx, nil +} + +func (tx BTCSlashingTx) Marshal() ([]byte, error) { + return tx, nil +} + +func (tx BTCSlashingTx) MustMarshal() []byte { + txBytes, err := tx.Marshal() + if err != nil { + panic(err) + } + return txBytes +} + +func (tx BTCSlashingTx) MarshalTo(data []byte) (int, error) { + bz, err := tx.Marshal() + if err != nil { + return 0, err + } + copy(data, bz) + return len(data), nil +} + +func (tx *BTCSlashingTx) Unmarshal(data []byte) error { + *tx = data + + // ensure data can be decoded to a tx + if _, err := tx.ToMsgTx(); err != nil { + return err + } + + return nil +} + +func (tx *BTCSlashingTx) Size() int { + return len(tx.MustMarshal()) +} + +func (tx *BTCSlashingTx) ToHexStr() string { + txBytes := tx.MustMarshal() + return hex.EncodeToString(txBytes) +} + +func (tx *BTCSlashingTx) ToMsgTx() (*wire.MsgTx, error) { + return bbn.NewBTCTxFromBytes(*tx) +} + +func (tx *BTCSlashingTx) MustGetTxHash() *chainhash.Hash { + msgTx, err := tx.ToMsgTx() + if err != nil { + panic(err) + } + txHash := msgTx.TxHash() + return &txHash +} + +// Sign generates a signature on the slashing tx +func (tx *BTCSlashingTx) Sign( + fundingTx *wire.MsgTx, + spendOutputIndex uint32, + slashingPkScriptPath []byte, + sk *btcec.PrivateKey, +) (*bbn.BIP340Signature, error) { + msgTx, err := tx.ToMsgTx() + if err != nil { + return nil, err + } + schnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + msgTx, + fundingTx, + spendOutputIndex, + slashingPkScriptPath, + sk, + ) + if err != nil { + return nil, err + } + return bbn.NewBIP340SignatureFromBTCSig(schnorrSig), nil +} + +// VerifySignature verifies a signature on the slashing tx signed by staker, finality provider, or covenant +func (tx *BTCSlashingTx) VerifySignature( + fundingPkScript []byte, + fundingAmount int64, + slashingPkScriptPath []byte, + pk *btcec.PublicKey, + sig *bbn.BIP340Signature, +) error { + msgTx, err := tx.ToMsgTx() + if err != nil { + return err + } + return btcstaking.VerifyTransactionSigWithOutputData( + msgTx, + fundingPkScript, + fundingAmount, + slashingPkScriptPath, + pk, + *sig, + ) +} + +// EncSign generates an adaptor signature on the slashing tx with finality provider's +// public key as encryption key +func (tx *BTCSlashingTx) EncSign( + fundingMsgTx *wire.MsgTx, + spendOutputIndex uint32, + slashingPkScriptPath []byte, + sk *btcec.PrivateKey, + encKey *asig.EncryptionKey, +) (*asig.AdaptorSignature, error) { + msgTx, err := tx.ToMsgTx() + if err != nil { + return nil, err + } + adaptorSig, err := btcstaking.EncSignTxWithOneScriptSpendInputStrict( + msgTx, + fundingMsgTx, + spendOutputIndex, + slashingPkScriptPath, + sk, + encKey, + ) + if err != nil { + return nil, err + } + + return adaptorSig, nil +} + +// EncVerifyAdaptorSignature verifies an adaptor signature on the slashing tx +// with the finality provider's public key as encryption key +func (tx *BTCSlashingTx) EncVerifyAdaptorSignature( + stakingPkScript []byte, + stakingAmount int64, + slashingPkScriptPath []byte, + pk *btcec.PublicKey, + encKey *asig.EncryptionKey, + sig *asig.AdaptorSignature, +) error { + msgTx, err := tx.ToMsgTx() + if err != nil { + return err + } + return btcstaking.EncVerifyTransactionSigWithOutputData( + msgTx, + stakingPkScript, + stakingAmount, + slashingPkScriptPath, + pk, + encKey, + sig, + ) +} + +// ParseEncVerifyAdaptorSignatures verifies a list of adaptor signatures, each +// encrypted by a restaked validator PK and signed by the given PK, w.r.t. the +// given funding output (in staking or unbonding tx), slashing spend info and +// slashing tx +// It returns a list of parsed adaptor signatures in case of successful verification +func (tx *BTCSlashingTx) ParseEncVerifyAdaptorSignatures( + fundingOut *wire.TxOut, + slashingSpendInfo *btcstaking.SpendInfo, + pk *bbn.BIP340PubKey, + valPKs []bbn.BIP340PubKey, + sigs [][]byte, +) ([]asig.AdaptorSignature, error) { + var adaptorSigs []asig.AdaptorSignature = make([]asig.AdaptorSignature, len(sigs)) + for i := range sigs { + sig := sigs[i] + adaptorSig, err := asig.NewAdaptorSignatureFromBytes(sig) + if err != nil { + return nil, err + } + encKey, err := asig.NewEncryptionKeyFromBTCPK(valPKs[i].MustToBTCPK()) + if err != nil { + return nil, err + } + err = tx.EncVerifyAdaptorSignature( + fundingOut.PkScript, + fundingOut.Value, + slashingSpendInfo.GetPkScriptPath(), + pk.MustToBTCPK(), + encKey, + adaptorSig, + ) + if err != nil { + return nil, ErrInvalidCovenantSig.Wrapf("err: %v", err) + } + adaptorSigs[i] = *adaptorSig + } + return adaptorSigs, nil +} + +// EncVerifyAdaptorSignatures verifies a list of adaptor signatures, each +// encrypted by a restaked validator PK and signed by the given PK, w.r.t. the +// given funding output (in staking or unbonding tx), slashing spend info and +// slashing tx +func (tx *BTCSlashingTx) EncVerifyAdaptorSignatures( + fundingOut *wire.TxOut, + slashingSpendInfo *btcstaking.SpendInfo, + pk *bbn.BIP340PubKey, + valPKs []bbn.BIP340PubKey, + sigs [][]byte, +) error { + + _, err := tx.ParseEncVerifyAdaptorSignatures(fundingOut, slashingSpendInfo, pk, valPKs, sigs) + if err != nil { + return err + } + + return nil + +} + +func (tx *BTCSlashingTx) BuildSlashingTxWithWitness( + fpSK *btcec.PrivateKey, + fundingMsgTx *wire.MsgTx, + outputIdx uint32, + delegatorSig *bbn.BIP340Signature, + covenantSigs []*asig.AdaptorSignature, + slashingPathSpendInfo *btcstaking.SpendInfo, +) (*wire.MsgTx, error) { + fpSig, err := tx.Sign(fundingMsgTx, outputIdx, slashingPathSpendInfo.GetPkScriptPath(), fpSK) + if err != nil { + return nil, fmt.Errorf("failed to sign slashing tx for the finality provider: %w", err) + } + + // decrypt covenant adaptor signature to Schnorr signature using finality provider's SK, + // then marshal + decKey, err := asig.NewDecyptionKeyFromBTCSK(fpSK) + if err != nil { + return nil, fmt.Errorf("failed to get decryption key from BTC SK: %w", err) + } + + var covSigs []*schnorr.Signature + for _, covenantSig := range covenantSigs { + if covenantSig != nil { + covSigs = append(covSigs, covenantSig.Decrypt(decKey)) + } else { + covSigs = append(covSigs, nil) + } + } + + // construct witness + witness, err := slashingPathSpendInfo.CreateSlashingPathWitness( + covSigs, + []*schnorr.Signature{fpSig.MustToBTCSig()}, // TODO: work with restaking + delegatorSig.MustToBTCSig(), + ) + if err != nil { + return nil, err + } + // add witness to slashing tx + slashingMsgTxWithWitness, err := tx.ToMsgTx() + if err != nil { + return nil, err + } + slashingMsgTxWithWitness.TxIn[0].Witness = witness + + return slashingMsgTxWithWitness, nil +} diff --git a/x/btcstaking/types/btc_slashing_tx_test.go b/x/btcstaking/types/btc_slashing_tx_test.go new file mode 100644 index 000000000..292613426 --- /dev/null +++ b/x/btcstaking/types/btc_slashing_tx_test.go @@ -0,0 +1,100 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + btctest "github.com/babylonchain/babylon/testutil/bitcoin" + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" +) + +func FuzzSlashingTxWithWitness(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + var ( + stakingValue = int64(2 * 10e8) + stakingTimeBlocks = uint16(5) + net = &chaincfg.SimNetParams + ) + + // slashing address and key paris + slashingAddress, err := datagen.GenRandomBTCAddress(r, net) + require.NoError(t, err) + // Generate a slashing rate in the range [0.1, 0.50] i.e., 10-50%. + // NOTE - if the rate is higher or lower, it may produce slashing or change outputs + // with value below the dust threshold, causing test failure. + // Our goal is not to test failure due to such extreme cases here; + // this is already covered in FuzzGeneratingValidStakingSlashingTx + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + + // TODO: test restaking + fpSK, fpPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + covenantSKs, covenantPKs, err := datagen.GenRandomBTCKeyPairs(r, 5) + require.NoError(t, err) + covenantQuorum := uint32(3) + slashingChangeLockTime := uint16(101) + + // generate staking/slashing tx + testStakingInfo := datagen.GenBTCStakingSlashingInfo( + r, + t, + net, + delSK, + []*btcec.PublicKey{fpPK}, + covenantPKs, + covenantQuorum, + stakingTimeBlocks, + stakingValue, + slashingAddress.EncodeAddress(), + slashingRate, + slashingChangeLockTime, + ) + + slashingTx := testStakingInfo.SlashingTx + stakingMsgTx := testStakingInfo.StakingTx + + slashingSpendInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + slashingPkScriptPath := slashingSpendInfo.GetPkScriptPath() + + // sign slashing tx + delSig, err := slashingTx.Sign(stakingMsgTx, 0, slashingPkScriptPath, delSK) + require.NoError(t, err) + + covenantSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + []*btcec.PublicKey{fpPK}, + stakingMsgTx, + slashingPkScriptPath, + slashingTx, + ) + require.NoError(t, err) + + bsParams := types.Params{ + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: covenantQuorum, + } + covSigs, err := types.GetOrderedCovenantSignatures(0, covenantSigs, &bsParams) + require.NoError(t, err) + + // create slashing tx with witness + slashingMsgTxWithWitness, err := slashingTx.BuildSlashingTxWithWitness(fpSK, stakingMsgTx, 0, delSig, covSigs, slashingSpendInfo) + require.NoError(t, err) + + // verify slashing tx with witness + btctest.AssertSlashingTxExecution(t, testStakingInfo.StakingInfo.StakingOutput, slashingMsgTxWithWitness) + }) +} diff --git a/x/btcstaking/types/btc_undelegation.go b/x/btcstaking/types/btc_undelegation.go new file mode 100644 index 000000000..fdbee5495 --- /dev/null +++ b/x/btcstaking/types/btc_undelegation.go @@ -0,0 +1,86 @@ +package types + +import ( + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" +) + +func (ud *BTCUndelegation) HasCovenantQuorumOnSlashing(quorum uint32) bool { + return len(ud.CovenantSlashingSigs) >= int(quorum) +} + +func (ud *BTCUndelegation) HasCovenantQuorumOnUnbonding(quorum uint32) bool { + return len(ud.CovenantUnbondingSigList) >= int(quorum) +} + +// IsSignedByCovMemberOnUnbonding checks whether the given covenant PK has signed the unbonding tx +func (ud *BTCUndelegation) IsSignedByCovMemberOnUnbonding(covPK *bbn.BIP340PubKey) bool { + for _, sigInfo := range ud.CovenantUnbondingSigList { + if sigInfo.Pk.Equals(covPK) { + return true + } + } + return false +} + +// IsSignedByCovMemberOnSlashing checks whether the given covenant PK has signed the slashing tx +func (ud *BTCUndelegation) IsSignedByCovMemberOnSlashing(covPK *bbn.BIP340PubKey) bool { + for _, sigInfo := range ud.CovenantSlashingSigs { + if sigInfo.CovPk.Equals(covPK) { + return true + } + } + return false +} + +func (ud *BTCUndelegation) IsSignedByCovMember(covPk *bbn.BIP340PubKey) bool { + return ud.IsSignedByCovMemberOnUnbonding(covPk) && ud.IsSignedByCovMemberOnSlashing(covPk) +} + +func (ud *BTCUndelegation) HasCovenantQuorums(covenantQuorum uint32) bool { + return ud.HasCovenantQuorumOnUnbonding(covenantQuorum) && + ud.HasCovenantQuorumOnSlashing(covenantQuorum) +} + +func (ud *BTCUndelegation) GetCovSlashingAdaptorSig( + covBTCPK *bbn.BIP340PubKey, + valIdx int, + quorum uint32, +) (*asig.AdaptorSignature, error) { + if !ud.HasCovenantQuorums(quorum) { + return nil, ErrInvalidDelegationState.Wrap("BTC undelegation does not have a covenant quorum yet") + } + for _, covASigs := range ud.CovenantSlashingSigs { + if covASigs.CovPk.Equals(covBTCPK) { + if valIdx >= len(covASigs.AdaptorSigs) { + return nil, ErrFpNotFound.Wrap("validator index is out of scope") + } + sigBytes := covASigs.AdaptorSigs[valIdx] + return asig.NewAdaptorSignatureFromBytes(sigBytes) + } + } + + return nil, ErrInvalidCovenantPK.Wrap("covenant PK is not found") +} + +// AddCovenantSigs adds a Schnorr signature on the unbonding tx, and +// a list of adaptor signatures on the unbonding slashing tx, each encrypted +// by a finality provider's PK this BTC delegation restakes to, from the given +// covenant +// It is up to the caller to ensure that given adaptor signatures are valid or +// that they were not added before +func (ud *BTCUndelegation) addCovenantSigs( + covPk *bbn.BIP340PubKey, + unbondingSig *bbn.BIP340Signature, + slashingSigs []asig.AdaptorSignature, +) { + covUnbondingSigInfo := &SignatureInfo{Pk: covPk, Sig: unbondingSig} + ud.CovenantUnbondingSigList = append(ud.CovenantUnbondingSigList, covUnbondingSigInfo) + + adaptorSigs := make([][]byte, 0, len(slashingSigs)) + for _, s := range slashingSigs { + adaptorSigs = append(adaptorSigs, s.MustMarshal()) + } + slashingSigsInfo := &CovenantAdaptorSignatures{CovPk: covPk, AdaptorSigs: adaptorSigs} + ud.CovenantSlashingSigs = append(ud.CovenantSlashingSigs, slashingSigsInfo) +} diff --git a/x/btcstaking/types/btc_undelegation_test.go b/x/btcstaking/types/btc_undelegation_test.go new file mode 100644 index 000000000..8b62027ed --- /dev/null +++ b/x/btcstaking/types/btc_undelegation_test.go @@ -0,0 +1,123 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + btctest "github.com/babylonchain/babylon/testutil/bitcoin" + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +func FuzzBTCUndelegation_SlashingTx(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + net := &chaincfg.SimNetParams + + delSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + fpSK, fpPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + fpPKList := []*btcec.PublicKey{fpPK} + + // (3, 5) covenant committee + covenantSKs, covenantPKs, err := datagen.GenRandomBTCKeyPairs(r, 5) + require.NoError(t, err) + covenantQuorum := uint32(3) + + stakingTimeBlocks := uint16(5) + stakingValue := int64(2 * 10e8) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SimNetParams) + require.NoError(t, err) + + slashingRate := sdkmath.LegacyNewDecWithPrec(int64(datagen.RandomInt(r, 41)+10), 2) + unbondingTime := uint16(100) + 1 + unbondingValue := stakingValue - 1000 + slashingChangeLockTime := unbondingTime + + // construct the BTC delegation with everything + btcDel, err := datagen.GenRandomBTCDelegation( + r, + t, + bbn.NewBIP340PKsFromBTCPKs(fpPKList), + delSK, + covenantSKs, + covenantQuorum, + slashingAddress.EncodeAddress(), + 1000, + uint64(1000+stakingTimeBlocks), + uint64(stakingValue), + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + + stakingTxHash := btcDel.MustGetStakingTxHash() + + testInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + t, + net, + delSK, + fpPKList, + covenantPKs, + covenantQuorum, + wire.NewOutPoint(&stakingTxHash, 0), + unbondingTime, + unbondingValue, + slashingAddress.EncodeAddress(), + slashingRate, + slashingChangeLockTime, + ) + require.NoError(t, err) + + // delegator signs the unbonding slashing tx + delSlashingTxSig, err := testInfo.GenDelSlashingTxSig(delSK) + require.NoError(t, err) + + unbondingTxBytes, err := bbn.SerializeBTCTx(testInfo.UnbondingTx) + require.NoError(t, err) + + // spend info of the unbonding slashing tx + unbondingSlashingSpendInfo, err := testInfo.UnbondingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // covenant signs (using adaptor signature) the slashing tx + covenantSigs, err := datagen.GenCovenantAdaptorSigs( + covenantSKs, + []*btcec.PublicKey{fpPK}, + testInfo.UnbondingTx, + unbondingSlashingSpendInfo.GetPkScriptPath(), + testInfo.SlashingTx, + ) + require.NoError(t, err) + btcDel.BtcUndelegation = &types.BTCUndelegation{ + UnbondingTx: unbondingTxBytes, + SlashingTx: testInfo.SlashingTx, + DelegatorUnbondingSig: nil, // not relevant here + DelegatorSlashingSig: delSlashingTxSig, + CovenantSlashingSigs: covenantSigs, + CovenantUnbondingSigList: nil, // not relevant here + } + + bsParams := &types.Params{ + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(covenantPKs), + CovenantQuorum: covenantQuorum, + } + + // build slashing tx with witness for spending the unbonding tx + unbondingSlashingTxWithWitness, err := btcDel.BuildUnbondingSlashingTxWithWitness(bsParams, net, fpSK) + require.NoError(t, err) + + // assert the execution + btctest.AssertSlashingTxExecution(t, testInfo.UnbondingInfo.UnbondingOutput, unbondingSlashingTxWithWitness) + }) +} diff --git a/x/btcstaking/types/btcstaking.go b/x/btcstaking/types/btcstaking.go new file mode 100644 index 000000000..880755870 --- /dev/null +++ b/x/btcstaking/types/btcstaking.go @@ -0,0 +1,130 @@ +package types + +import ( + "fmt" + + "sort" + + "cosmossdk.io/math" + asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" +) + +func (fp *FinalityProvider) IsSlashed() bool { + return fp.SlashedBabylonHeight > 0 +} + +func (fp *FinalityProvider) ValidateBasic() error { + // ensure fields are non-empty and well-formatted + if fp.BabylonPk == nil { + return fmt.Errorf("empty Babylon public key") + } + if fp.BtcPk == nil { + return fmt.Errorf("empty BTC public key") + } + if _, err := fp.BtcPk.ToBTCPK(); err != nil { + return fmt.Errorf("BtcPk is not correctly formatted: %w", err) + } + if fp.Pop == nil { + return fmt.Errorf("empty proof of possession") + } + if err := fp.Pop.ValidateBasic(); err != nil { + return err + } + + return nil +} + +// FilterTopNFinalityProviders returns the top n finality providers based on VotingPower. +func FilterTopNFinalityProviders(fps []*FinalityProviderWithMeta, n uint32) []*FinalityProviderWithMeta { + numFps := uint32(len(fps)) + + // if the given finality provider set is no bigger than n, no need to do anything + if numFps <= n { + return fps + } + + // Sort the finality providers slice, from higher to lower voting power + sort.SliceStable(fps, func(i, j int) bool { + return fps[i].VotingPower > fps[j].VotingPower + }) + + // Return the top n elements + return fps[:n] +} + +func ExistsDup(btcPKs []bbn.BIP340PubKey) bool { + seen := make(map[string]struct{}) + + for _, btcPK := range btcPKs { + pkStr := string(btcPK) + if _, found := seen[pkStr]; found { + return true + } else { + seen[pkStr] = struct{}{} + } + } + + return false +} + +func NewSignatureInfo(pk *bbn.BIP340PubKey, sig *bbn.BIP340Signature) *SignatureInfo { + return &SignatureInfo{ + Pk: pk, + Sig: sig, + } +} + +// GetOrderedCovenantSignatures returns the ordered covenant adaptor signatures +// encrypted by the finality provider's PK at the given index from the given list of +// covenant signatures +// the order of covenant adaptor signatures will follow the reverse lexicographical order +// of signing public keys, in order to be used as tx witness +func GetOrderedCovenantSignatures(fpIdx int, covSigsList []*CovenantAdaptorSignatures, params *Params) ([]*asig.AdaptorSignature, error) { + // construct the map where key is the covenant PK and value is this + // covenant member's adaptor signature encrypted by the given finality provider's PK + covSigsMap := map[string]*asig.AdaptorSignature{} + for _, covSigs := range covSigsList { + // find the adaptor signature at the corresponding finality provider's index + if fpIdx >= len(covSigs.AdaptorSigs) { + return nil, fmt.Errorf("finality provider index is out of the scope") + } + covSigBytes := covSigs.AdaptorSigs[fpIdx] + // decode the adaptor signature bytes + covSig, err := asig.NewAdaptorSignatureFromBytes(covSigBytes) + if err != nil { + return nil, err + } + // append to map + covSigsMap[covSigs.CovPk.MarshalHex()] = covSig + } + + // sort covenant PKs in reverse reverse lexicographical order + orderedCovenantPKs := bbn.SortBIP340PKs(params.CovenantPks) + + // get ordered list of covenant signatures w.r.t. the order of sorted covenant PKs + // Note that only a quorum number of covenant signatures needs to be provided + orderedCovSigs := []*asig.AdaptorSignature{} + for _, covPK := range orderedCovenantPKs { + if covSig, ok := covSigsMap[covPK.MarshalHex()]; ok { + orderedCovSigs = append(orderedCovSigs, covSig) + } else { + orderedCovSigs = append(orderedCovSigs, nil) + } + } + + return orderedCovSigs, nil +} + +// MinimumUnbondingTime returns the minimum unbonding time. It is the bigger value from: +// - MinUnbondingTime +// - CheckpointFinalizationTimeout +func MinimumUnbondingTime( + stakingParams Params, + checkpointingParams btcctypes.Params) uint64 { + return math.Max[uint64]( + uint64(stakingParams.MinUnbondingTime), + checkpointingParams.CheckpointFinalizationTimeout, + ) +} diff --git a/x/btcstaking/types/btcstaking.pb.go b/x/btcstaking/types/btcstaking.pb.go new file mode 100644 index 000000000..c377002ae --- /dev/null +++ b/x/btcstaking/types/btcstaking.pb.go @@ -0,0 +1,3776 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/btcstaking.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "github.com/cosmos/cosmos-proto" + secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + types "github.com/cosmos/cosmos-sdk/x/staking/types" + _ "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 + +// BTCDelegationStatus is the status of a delegation. The state transition path is +// PENDING -> ACTIVE -> UNBONDED with two possibilities: +// 1. the typical path when timelock of staking transaction expires. +// 2. the path when staker requests early undelegation through MsgBTCUndelegate message. +type BTCDelegationStatus int32 + +const ( + // PENDING defines a delegation that is waiting for covenant signatures to become active. + BTCDelegationStatus_PENDING BTCDelegationStatus = 0 + // ACTIVE defines a delegation that has voting power + BTCDelegationStatus_ACTIVE BTCDelegationStatus = 1 + // UNBONDED defines a delegation no longer has voting power: + // - either reaching the end of staking transaction timelock + // - or receiving unbonding tx with signatures from staker and covenant committee + BTCDelegationStatus_UNBONDED BTCDelegationStatus = 2 + // ANY is any of the above status + BTCDelegationStatus_ANY BTCDelegationStatus = 3 +) + +var BTCDelegationStatus_name = map[int32]string{ + 0: "PENDING", + 1: "ACTIVE", + 2: "UNBONDED", + 3: "ANY", +} + +var BTCDelegationStatus_value = map[string]int32{ + "PENDING": 0, + "ACTIVE": 1, + "UNBONDED": 2, + "ANY": 3, +} + +func (x BTCDelegationStatus) String() string { + return proto.EnumName(BTCDelegationStatus_name, int32(x)) +} + +func (BTCDelegationStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{0} +} + +// FinalityProvider defines a finality provider +type FinalityProvider struct { + // description defines the description terms for the finality provider. + Description *types.Description `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` + // commission defines the commission rate of the finality provider. + Commission *cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=commission,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"commission,omitempty"` + // babylon_pk is the Babylon secp256k1 PK of this finality provider + BabylonPk *secp256k1.PubKey `protobuf:"bytes,3,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,4,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // pop is the proof of possession of babylon_pk and btc_pk + Pop *ProofOfPossession `protobuf:"bytes,5,opt,name=pop,proto3" json:"pop,omitempty"` + // slashed_babylon_height indicates the Babylon height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + SlashedBabylonHeight uint64 `protobuf:"varint,6,opt,name=slashed_babylon_height,json=slashedBabylonHeight,proto3" json:"slashed_babylon_height,omitempty"` + // slashed_btc_height indicates the BTC height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + SlashedBtcHeight uint64 `protobuf:"varint,7,opt,name=slashed_btc_height,json=slashedBtcHeight,proto3" json:"slashed_btc_height,omitempty"` +} + +func (m *FinalityProvider) Reset() { *m = FinalityProvider{} } +func (m *FinalityProvider) String() string { return proto.CompactTextString(m) } +func (*FinalityProvider) ProtoMessage() {} +func (*FinalityProvider) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{0} +} +func (m *FinalityProvider) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FinalityProvider) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FinalityProvider.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 *FinalityProvider) XXX_Merge(src proto.Message) { + xxx_messageInfo_FinalityProvider.Merge(m, src) +} +func (m *FinalityProvider) XXX_Size() int { + return m.Size() +} +func (m *FinalityProvider) XXX_DiscardUnknown() { + xxx_messageInfo_FinalityProvider.DiscardUnknown(m) +} + +var xxx_messageInfo_FinalityProvider proto.InternalMessageInfo + +func (m *FinalityProvider) GetDescription() *types.Description { + if m != nil { + return m.Description + } + return nil +} + +func (m *FinalityProvider) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *FinalityProvider) GetPop() *ProofOfPossession { + if m != nil { + return m.Pop + } + return nil +} + +func (m *FinalityProvider) GetSlashedBabylonHeight() uint64 { + if m != nil { + return m.SlashedBabylonHeight + } + return 0 +} + +func (m *FinalityProvider) GetSlashedBtcHeight() uint64 { + if m != nil { + return m.SlashedBtcHeight + } + return 0 +} + +// FinalityProviderWithMeta wraps the FinalityProvider with metadata. +type FinalityProviderWithMeta struct { + // btc_pk is the Bitcoin secp256k1 PK of thisfinality provider + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // height is the queried Babylon height + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + // voting_power is the voting power of this finality provider at the given height + VotingPower uint64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` + // slashed_babylon_height indicates the Babylon height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + SlashedBabylonHeight uint64 `protobuf:"varint,4,opt,name=slashed_babylon_height,json=slashedBabylonHeight,proto3" json:"slashed_babylon_height,omitempty"` + // slashed_btc_height indicates the BTC height when + // the finality provider is slashed. + // if it's 0 then the finality provider is not slashed + SlashedBtcHeight uint64 `protobuf:"varint,5,opt,name=slashed_btc_height,json=slashedBtcHeight,proto3" json:"slashed_btc_height,omitempty"` +} + +func (m *FinalityProviderWithMeta) Reset() { *m = FinalityProviderWithMeta{} } +func (m *FinalityProviderWithMeta) String() string { return proto.CompactTextString(m) } +func (*FinalityProviderWithMeta) ProtoMessage() {} +func (*FinalityProviderWithMeta) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{1} +} +func (m *FinalityProviderWithMeta) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FinalityProviderWithMeta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FinalityProviderWithMeta.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 *FinalityProviderWithMeta) XXX_Merge(src proto.Message) { + xxx_messageInfo_FinalityProviderWithMeta.Merge(m, src) +} +func (m *FinalityProviderWithMeta) XXX_Size() int { + return m.Size() +} +func (m *FinalityProviderWithMeta) XXX_DiscardUnknown() { + xxx_messageInfo_FinalityProviderWithMeta.DiscardUnknown(m) +} + +var xxx_messageInfo_FinalityProviderWithMeta proto.InternalMessageInfo + +func (m *FinalityProviderWithMeta) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *FinalityProviderWithMeta) GetVotingPower() uint64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +func (m *FinalityProviderWithMeta) GetSlashedBabylonHeight() uint64 { + if m != nil { + return m.SlashedBabylonHeight + } + return 0 +} + +func (m *FinalityProviderWithMeta) GetSlashedBtcHeight() uint64 { + if m != nil { + return m.SlashedBtcHeight + } + return 0 +} + +// BTCDelegation defines a BTC delegation +type BTCDelegation struct { + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + BabylonPk *secp256k1.PubKey `protobuf:"bytes,1,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // pop is the proof of possession of babylon_pk and btc_pk + Pop *ProofOfPossession `protobuf:"bytes,3,opt,name=pop,proto3" json:"pop,omitempty"` + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + FpBtcPkList []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,4,rep,name=fp_btc_pk_list,json=fpBtcPkList,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk_list,omitempty"` + // start_height is the start BTC height of the BTC delegation + // it is the start BTC height of the timelock + StartHeight uint64 `protobuf:"varint,5,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + // end_height is the end height of the BTC delegation + // it is the end BTC height of the timelock - w + EndHeight uint64 `protobuf:"varint,6,opt,name=end_height,json=endHeight,proto3" json:"end_height,omitempty"` + // total_sat is the total amount of BTC stakes in this delegation + // quantified in satoshi + TotalSat uint64 `protobuf:"varint,7,opt,name=total_sat,json=totalSat,proto3" json:"total_sat,omitempty"` + // staking_tx is the staking tx + StakingTx []byte `protobuf:"bytes,8,opt,name=staking_tx,json=stakingTx,proto3" json:"staking_tx,omitempty"` + // staking_output_idx is the index of the staking output in the staking tx + StakingOutputIdx uint32 `protobuf:"varint,9,opt,name=staking_output_idx,json=stakingOutputIdx,proto3" json:"staking_output_idx,omitempty"` + // slashing_tx is the slashing tx + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + SlashingTx *BTCSlashingTx `protobuf:"bytes,10,opt,name=slashing_tx,json=slashingTx,proto3,customtype=BTCSlashingTx" json:"slashing_tx,omitempty"` + // delegator_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + DelegatorSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,11,opt,name=delegator_sig,json=delegatorSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_sig,omitempty"` + // covenant_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + CovenantSigs []*CovenantAdaptorSignatures `protobuf:"bytes,12,rep,name=covenant_sigs,json=covenantSigs,proto3" json:"covenant_sigs,omitempty"` + // unbonding_time describes how long the funds will be locked either in unbonding output + // or slashing change output + UnbondingTime uint32 `protobuf:"varint,13,opt,name=unbonding_time,json=unbondingTime,proto3" json:"unbonding_time,omitempty"` + // btc_undelegation is the information about the early unbonding path of the BTC delegation + BtcUndelegation *BTCUndelegation `protobuf:"bytes,14,opt,name=btc_undelegation,json=btcUndelegation,proto3" json:"btc_undelegation,omitempty"` +} + +func (m *BTCDelegation) Reset() { *m = BTCDelegation{} } +func (m *BTCDelegation) String() string { return proto.CompactTextString(m) } +func (*BTCDelegation) ProtoMessage() {} +func (*BTCDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{2} +} +func (m *BTCDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCDelegation.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 *BTCDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCDelegation.Merge(m, src) +} +func (m *BTCDelegation) XXX_Size() int { + return m.Size() +} +func (m *BTCDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_BTCDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCDelegation proto.InternalMessageInfo + +func (m *BTCDelegation) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *BTCDelegation) GetPop() *ProofOfPossession { + if m != nil { + return m.Pop + } + return nil +} + +func (m *BTCDelegation) GetStartHeight() uint64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +func (m *BTCDelegation) GetEndHeight() uint64 { + if m != nil { + return m.EndHeight + } + return 0 +} + +func (m *BTCDelegation) GetTotalSat() uint64 { + if m != nil { + return m.TotalSat + } + return 0 +} + +func (m *BTCDelegation) GetStakingTx() []byte { + if m != nil { + return m.StakingTx + } + return nil +} + +func (m *BTCDelegation) GetStakingOutputIdx() uint32 { + if m != nil { + return m.StakingOutputIdx + } + return 0 +} + +func (m *BTCDelegation) GetCovenantSigs() []*CovenantAdaptorSignatures { + if m != nil { + return m.CovenantSigs + } + return nil +} + +func (m *BTCDelegation) GetUnbondingTime() uint32 { + if m != nil { + return m.UnbondingTime + } + return 0 +} + +func (m *BTCDelegation) GetBtcUndelegation() *BTCUndelegation { + if m != nil { + return m.BtcUndelegation + } + return nil +} + +// BTCUndelegation contains the information about the early unbonding path of the BTC delegation +type BTCUndelegation struct { + // unbonding_tx is the transaction which will transfer the funds from staking + // output to unbonding output. Unbonding output will usually have lower timelock + // than staking output. + UnbondingTx []byte `protobuf:"bytes,1,opt,name=unbonding_tx,json=unbondingTx,proto3" json:"unbonding_tx,omitempty"` + // slashing_tx is the slashing tx for unbonding transactions + // It is partially signed by SK corresponding to btc_pk, but not signed by + // finality provider or covenant yet. + SlashingTx *BTCSlashingTx `protobuf:"bytes,2,opt,name=slashing_tx,json=slashingTx,proto3,customtype=BTCSlashingTx" json:"slashing_tx,omitempty"` + // delegator_unbonding_sig is the signature on the unbonding tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It effectively proves that the delegator wants to unbond and thus + // Babylon will consider this BTC delegation unbonded. Delegator's BTC + // on Bitcoin will be unbonded after timelock + DelegatorUnbondingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,3,opt,name=delegator_unbonding_sig,json=delegatorUnbondingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_unbonding_sig,omitempty"` + // delegator_slashing_sig is the signature on the slashing tx + // by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the unbonding tx output. + DelegatorSlashingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,4,opt,name=delegator_slashing_sig,json=delegatorSlashingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_slashing_sig,omitempty"` + // covenant_slashing_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + CovenantSlashingSigs []*CovenantAdaptorSignatures `protobuf:"bytes,5,rep,name=covenant_slashing_sigs,json=covenantSlashingSigs,proto3" json:"covenant_slashing_sigs,omitempty"` + // covenant_unbonding_sig_list is the list of signatures on the unbonding tx + // by covenant members + // It must be provided after processing undelagate message by Babylon + CovenantUnbondingSigList []*SignatureInfo `protobuf:"bytes,6,rep,name=covenant_unbonding_sig_list,json=covenantUnbondingSigList,proto3" json:"covenant_unbonding_sig_list,omitempty"` +} + +func (m *BTCUndelegation) Reset() { *m = BTCUndelegation{} } +func (m *BTCUndelegation) String() string { return proto.CompactTextString(m) } +func (*BTCUndelegation) ProtoMessage() {} +func (*BTCUndelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{3} +} +func (m *BTCUndelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCUndelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCUndelegation.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 *BTCUndelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCUndelegation.Merge(m, src) +} +func (m *BTCUndelegation) XXX_Size() int { + return m.Size() +} +func (m *BTCUndelegation) XXX_DiscardUnknown() { + xxx_messageInfo_BTCUndelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCUndelegation proto.InternalMessageInfo + +func (m *BTCUndelegation) GetUnbondingTx() []byte { + if m != nil { + return m.UnbondingTx + } + return nil +} + +func (m *BTCUndelegation) GetCovenantSlashingSigs() []*CovenantAdaptorSignatures { + if m != nil { + return m.CovenantSlashingSigs + } + return nil +} + +func (m *BTCUndelegation) GetCovenantUnbondingSigList() []*SignatureInfo { + if m != nil { + return m.CovenantUnbondingSigList + } + return nil +} + +// BTCUndelegationInfo provides all necessary info about the undeleagation +type BTCUndelegationInfo struct { + // unbonding_tx is the transaction which will transfer the funds from staking + // output to unbonding output. Unbonding output will usually have lower timelock + // than staking output. + UnbondingTx []byte `protobuf:"bytes,1,opt,name=unbonding_tx,json=unbondingTx,proto3" json:"unbonding_tx,omitempty"` + // covenant_unbonding_sig_list is the list of signatures on the unbonding tx + // by covenant members + CovenantUnbondingSigList []*SignatureInfo `protobuf:"bytes,2,rep,name=covenant_unbonding_sig_list,json=covenantUnbondingSigList,proto3" json:"covenant_unbonding_sig_list,omitempty"` + // covenant_slashing_sigs is a list of adaptor signatures on the + // unbonding slashing tx by each covenant member + // It will be a part of the witness for the staking tx output. + CovenantSlashingSigs []*CovenantAdaptorSignatures `protobuf:"bytes,3,rep,name=covenant_slashing_sigs,json=covenantSlashingSigs,proto3" json:"covenant_slashing_sigs,omitempty"` +} + +func (m *BTCUndelegationInfo) Reset() { *m = BTCUndelegationInfo{} } +func (m *BTCUndelegationInfo) String() string { return proto.CompactTextString(m) } +func (*BTCUndelegationInfo) ProtoMessage() {} +func (*BTCUndelegationInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{4} +} +func (m *BTCUndelegationInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCUndelegationInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCUndelegationInfo.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 *BTCUndelegationInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCUndelegationInfo.Merge(m, src) +} +func (m *BTCUndelegationInfo) XXX_Size() int { + return m.Size() +} +func (m *BTCUndelegationInfo) XXX_DiscardUnknown() { + xxx_messageInfo_BTCUndelegationInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCUndelegationInfo proto.InternalMessageInfo + +func (m *BTCUndelegationInfo) GetUnbondingTx() []byte { + if m != nil { + return m.UnbondingTx + } + return nil +} + +func (m *BTCUndelegationInfo) GetCovenantUnbondingSigList() []*SignatureInfo { + if m != nil { + return m.CovenantUnbondingSigList + } + return nil +} + +func (m *BTCUndelegationInfo) GetCovenantSlashingSigs() []*CovenantAdaptorSignatures { + if m != nil { + return m.CovenantSlashingSigs + } + return nil +} + +// BTCDelegatorDelegations is a collection of BTC delegations from the same delegator. +type BTCDelegatorDelegations struct { + Dels []*BTCDelegation `protobuf:"bytes,1,rep,name=dels,proto3" json:"dels,omitempty"` +} + +func (m *BTCDelegatorDelegations) Reset() { *m = BTCDelegatorDelegations{} } +func (m *BTCDelegatorDelegations) String() string { return proto.CompactTextString(m) } +func (*BTCDelegatorDelegations) ProtoMessage() {} +func (*BTCDelegatorDelegations) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{5} +} +func (m *BTCDelegatorDelegations) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCDelegatorDelegations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCDelegatorDelegations.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 *BTCDelegatorDelegations) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCDelegatorDelegations.Merge(m, src) +} +func (m *BTCDelegatorDelegations) XXX_Size() int { + return m.Size() +} +func (m *BTCDelegatorDelegations) XXX_DiscardUnknown() { + xxx_messageInfo_BTCDelegatorDelegations.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCDelegatorDelegations proto.InternalMessageInfo + +func (m *BTCDelegatorDelegations) GetDels() []*BTCDelegation { + if m != nil { + return m.Dels + } + return nil +} + +// BTCDelegatorDelegationIndex is a list of staking tx hashes of BTC delegations from the same delegator. +type BTCDelegatorDelegationIndex struct { + StakingTxHashList [][]byte `protobuf:"bytes,1,rep,name=staking_tx_hash_list,json=stakingTxHashList,proto3" json:"staking_tx_hash_list,omitempty"` +} + +func (m *BTCDelegatorDelegationIndex) Reset() { *m = BTCDelegatorDelegationIndex{} } +func (m *BTCDelegatorDelegationIndex) String() string { return proto.CompactTextString(m) } +func (*BTCDelegatorDelegationIndex) ProtoMessage() {} +func (*BTCDelegatorDelegationIndex) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{6} +} +func (m *BTCDelegatorDelegationIndex) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCDelegatorDelegationIndex) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCDelegatorDelegationIndex.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 *BTCDelegatorDelegationIndex) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCDelegatorDelegationIndex.Merge(m, src) +} +func (m *BTCDelegatorDelegationIndex) XXX_Size() int { + return m.Size() +} +func (m *BTCDelegatorDelegationIndex) XXX_DiscardUnknown() { + xxx_messageInfo_BTCDelegatorDelegationIndex.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCDelegatorDelegationIndex proto.InternalMessageInfo + +func (m *BTCDelegatorDelegationIndex) GetStakingTxHashList() [][]byte { + if m != nil { + return m.StakingTxHashList + } + return nil +} + +// SignatureInfo is a BIP-340 signature together with its signer's BIP-340 PK +type SignatureInfo struct { + Pk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=pk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"pk,omitempty"` + Sig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,2,opt,name=sig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"sig,omitempty"` +} + +func (m *SignatureInfo) Reset() { *m = SignatureInfo{} } +func (m *SignatureInfo) String() string { return proto.CompactTextString(m) } +func (*SignatureInfo) ProtoMessage() {} +func (*SignatureInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{7} +} +func (m *SignatureInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureInfo.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 *SignatureInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureInfo.Merge(m, src) +} +func (m *SignatureInfo) XXX_Size() int { + return m.Size() +} +func (m *SignatureInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureInfo proto.InternalMessageInfo + +// CovenantAdaptorSignatures is a list adaptor signatures signed by the +// covenant with different finality provider's public keys as encryption keys +type CovenantAdaptorSignatures struct { + // cov_pk is the public key of the covenant emulator, used as the public key of the adaptor signature + CovPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=cov_pk,json=covPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"cov_pk,omitempty"` + // adaptor_sigs is a list of adaptor signatures, each encrypted by a restaked BTC finality provider's public key + AdaptorSigs [][]byte `protobuf:"bytes,2,rep,name=adaptor_sigs,json=adaptorSigs,proto3" json:"adaptor_sigs,omitempty"` +} + +func (m *CovenantAdaptorSignatures) Reset() { *m = CovenantAdaptorSignatures{} } +func (m *CovenantAdaptorSignatures) String() string { return proto.CompactTextString(m) } +func (*CovenantAdaptorSignatures) ProtoMessage() {} +func (*CovenantAdaptorSignatures) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{8} +} +func (m *CovenantAdaptorSignatures) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CovenantAdaptorSignatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CovenantAdaptorSignatures.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 *CovenantAdaptorSignatures) XXX_Merge(src proto.Message) { + xxx_messageInfo_CovenantAdaptorSignatures.Merge(m, src) +} +func (m *CovenantAdaptorSignatures) XXX_Size() int { + return m.Size() +} +func (m *CovenantAdaptorSignatures) XXX_DiscardUnknown() { + xxx_messageInfo_CovenantAdaptorSignatures.DiscardUnknown(m) +} + +var xxx_messageInfo_CovenantAdaptorSignatures proto.InternalMessageInfo + +func (m *CovenantAdaptorSignatures) GetAdaptorSigs() [][]byte { + if m != nil { + return m.AdaptorSigs + } + return nil +} + +// SelectiveSlashingEvidence is the evidence that the finality provider +// selectively slashed a BTC delegation +// NOTE: it's possible that a slashed finality provider exploits the +// SelectiveSlashingEvidence endpoint while it is actually slashed due to +// equivocation. But such behaviour does not affect the system's security +// or gives any benefit for the adversary +type SelectiveSlashingEvidence struct { + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + StakingTxHash string `protobuf:"bytes,1,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"` + // fp_btc_pk is the BTC PK of the finality provider who + // launches the selective slashing offence + FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"` + // recovered_fp_btc_sk is the finality provider's BTC SK recovered from + // the covenant adaptor/Schnorr signature pair. It is the consequence + // of selective slashing. + RecoveredFpBtcSk []byte `protobuf:"bytes,3,opt,name=recovered_fp_btc_sk,json=recoveredFpBtcSk,proto3" json:"recovered_fp_btc_sk,omitempty"` +} + +func (m *SelectiveSlashingEvidence) Reset() { *m = SelectiveSlashingEvidence{} } +func (m *SelectiveSlashingEvidence) String() string { return proto.CompactTextString(m) } +func (*SelectiveSlashingEvidence) ProtoMessage() {} +func (*SelectiveSlashingEvidence) Descriptor() ([]byte, []int) { + return fileDescriptor_3851ae95ccfaf7db, []int{9} +} +func (m *SelectiveSlashingEvidence) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SelectiveSlashingEvidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SelectiveSlashingEvidence.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 *SelectiveSlashingEvidence) XXX_Merge(src proto.Message) { + xxx_messageInfo_SelectiveSlashingEvidence.Merge(m, src) +} +func (m *SelectiveSlashingEvidence) XXX_Size() int { + return m.Size() +} +func (m *SelectiveSlashingEvidence) XXX_DiscardUnknown() { + xxx_messageInfo_SelectiveSlashingEvidence.DiscardUnknown(m) +} + +var xxx_messageInfo_SelectiveSlashingEvidence proto.InternalMessageInfo + +func (m *SelectiveSlashingEvidence) GetStakingTxHash() string { + if m != nil { + return m.StakingTxHash + } + return "" +} + +func (m *SelectiveSlashingEvidence) GetRecoveredFpBtcSk() []byte { + if m != nil { + return m.RecoveredFpBtcSk + } + return nil +} + +func init() { + proto.RegisterEnum("babylon.btcstaking.v1.BTCDelegationStatus", BTCDelegationStatus_name, BTCDelegationStatus_value) + proto.RegisterType((*FinalityProvider)(nil), "babylon.btcstaking.v1.FinalityProvider") + proto.RegisterType((*FinalityProviderWithMeta)(nil), "babylon.btcstaking.v1.FinalityProviderWithMeta") + proto.RegisterType((*BTCDelegation)(nil), "babylon.btcstaking.v1.BTCDelegation") + proto.RegisterType((*BTCUndelegation)(nil), "babylon.btcstaking.v1.BTCUndelegation") + proto.RegisterType((*BTCUndelegationInfo)(nil), "babylon.btcstaking.v1.BTCUndelegationInfo") + proto.RegisterType((*BTCDelegatorDelegations)(nil), "babylon.btcstaking.v1.BTCDelegatorDelegations") + proto.RegisterType((*BTCDelegatorDelegationIndex)(nil), "babylon.btcstaking.v1.BTCDelegatorDelegationIndex") + proto.RegisterType((*SignatureInfo)(nil), "babylon.btcstaking.v1.SignatureInfo") + proto.RegisterType((*CovenantAdaptorSignatures)(nil), "babylon.btcstaking.v1.CovenantAdaptorSignatures") + proto.RegisterType((*SelectiveSlashingEvidence)(nil), "babylon.btcstaking.v1.SelectiveSlashingEvidence") +} + +func init() { + proto.RegisterFile("babylon/btcstaking/v1/btcstaking.proto", fileDescriptor_3851ae95ccfaf7db) +} + +var fileDescriptor_3851ae95ccfaf7db = []byte{ + // 1203 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcb, 0x6e, 0xdb, 0x46, + 0x1b, 0x35, 0x25, 0x59, 0x8e, 0x3e, 0x49, 0x89, 0x32, 0x71, 0x1c, 0x26, 0xc6, 0x6f, 0xfb, 0x57, + 0xd3, 0x40, 0x28, 0x1a, 0x2a, 0x76, 0x2e, 0x68, 0xbb, 0x28, 0x10, 0x59, 0x4e, 0x63, 0x24, 0x71, + 0x54, 0xca, 0x6e, 0xd1, 0x16, 0x28, 0x41, 0x91, 0x23, 0x8a, 0x90, 0xc4, 0x61, 0x39, 0x23, 0x55, + 0x7a, 0x80, 0x02, 0xdd, 0x14, 0xe8, 0xb6, 0xfb, 0x3e, 0x42, 0x9f, 0xa1, 0xc8, 0x32, 0xe8, 0xa6, + 0x85, 0x17, 0x46, 0x91, 0xbc, 0x48, 0x31, 0xc3, 0xe1, 0x45, 0x4e, 0xec, 0x5c, 0xe4, 0x9d, 0x38, + 0xdf, 0xed, 0xcc, 0x39, 0x87, 0x9c, 0x11, 0xdc, 0xe8, 0x98, 0x9d, 0xe9, 0x80, 0x78, 0xf5, 0x0e, + 0xb3, 0x28, 0x33, 0xfb, 0xae, 0xe7, 0xd4, 0xc7, 0x9b, 0xa9, 0x27, 0xcd, 0x0f, 0x08, 0x23, 0xe8, + 0xb2, 0xcc, 0xd3, 0x52, 0x91, 0xf1, 0xe6, 0xb5, 0x65, 0x87, 0x38, 0x44, 0x64, 0xd4, 0xf9, 0xaf, + 0x30, 0xf9, 0xda, 0x55, 0x8b, 0xd0, 0x21, 0xa1, 0x46, 0x18, 0x08, 0x1f, 0x64, 0xa8, 0x1a, 0x3e, + 0xd5, 0xad, 0x60, 0xea, 0x33, 0x52, 0xa7, 0xd8, 0xf2, 0xb7, 0xee, 0xde, 0xeb, 0x6f, 0xd6, 0xfb, + 0x78, 0x1a, 0xe5, 0x5c, 0x97, 0x39, 0x09, 0x9e, 0x0e, 0x66, 0xe6, 0x66, 0x7d, 0x06, 0xd1, 0xb5, + 0xf5, 0xd7, 0x23, 0xf7, 0x89, 0x1f, 0x26, 0x54, 0xff, 0xce, 0x42, 0xe5, 0x81, 0xeb, 0x99, 0x03, + 0x97, 0x4d, 0x5b, 0x01, 0x19, 0xbb, 0x36, 0x0e, 0xd0, 0x0e, 0x14, 0x6d, 0x4c, 0xad, 0xc0, 0xf5, + 0x99, 0x4b, 0x3c, 0x55, 0xd9, 0x50, 0x6a, 0xc5, 0xad, 0x0f, 0x34, 0x89, 0x31, 0xd9, 0x99, 0x98, + 0xa8, 0x35, 0x93, 0x54, 0x3d, 0x5d, 0x87, 0x9e, 0x00, 0x58, 0x64, 0x38, 0x74, 0x29, 0xe5, 0x5d, + 0x32, 0x1b, 0x4a, 0xad, 0xd0, 0xb8, 0x79, 0x78, 0xb4, 0xbe, 0x1a, 0x36, 0xa2, 0x76, 0x5f, 0x73, + 0x49, 0x7d, 0x68, 0xb2, 0x9e, 0xf6, 0x18, 0x3b, 0xa6, 0x35, 0x6d, 0x62, 0xeb, 0xaf, 0x3f, 0x6e, + 0x82, 0x9c, 0xd3, 0xc4, 0x96, 0x9e, 0x6a, 0x80, 0x3e, 0x07, 0x90, 0xbb, 0x31, 0xfc, 0xbe, 0x9a, + 0x15, 0xa0, 0xd6, 0x23, 0x50, 0x21, 0x55, 0x5a, 0x4c, 0x95, 0xd6, 0x1a, 0x75, 0x1e, 0xe1, 0xa9, + 0x5e, 0x90, 0x25, 0xad, 0x3e, 0x7a, 0x02, 0xf9, 0x0e, 0xb3, 0x78, 0x6d, 0x6e, 0x43, 0xa9, 0x95, + 0x1a, 0xf7, 0x0e, 0x8f, 0xd6, 0xb7, 0x1c, 0x97, 0xf5, 0x46, 0x1d, 0xcd, 0x22, 0xc3, 0xba, 0xcc, + 0xb4, 0x7a, 0xa6, 0xeb, 0x45, 0x0f, 0x75, 0x36, 0xf5, 0x31, 0xd5, 0x1a, 0xbb, 0xad, 0xdb, 0x77, + 0x6e, 0xc9, 0x96, 0x8b, 0x1d, 0x66, 0xb5, 0xfa, 0xe8, 0x33, 0xc8, 0xfa, 0xc4, 0x57, 0x17, 0x05, + 0x8e, 0x9a, 0xf6, 0x5a, 0xe9, 0xb5, 0x56, 0x40, 0x48, 0xf7, 0x69, 0xb7, 0x45, 0x28, 0xc5, 0x62, + 0x17, 0x3a, 0x2f, 0x42, 0x77, 0x60, 0x85, 0x0e, 0x4c, 0xda, 0xc3, 0xb6, 0x11, 0x6d, 0xa9, 0x87, + 0x5d, 0xa7, 0xc7, 0xd4, 0xfc, 0x86, 0x52, 0xcb, 0xe9, 0xcb, 0x32, 0xda, 0x08, 0x83, 0x0f, 0x45, + 0x0c, 0x7d, 0x0c, 0x28, 0xae, 0x62, 0x56, 0x54, 0xb1, 0x24, 0x2a, 0x2a, 0x51, 0x05, 0xb3, 0xc2, + 0xec, 0xea, 0xcf, 0x19, 0x50, 0x8f, 0x2b, 0xfb, 0xb5, 0xcb, 0x7a, 0x4f, 0x30, 0x33, 0x53, 0x5c, + 0x28, 0x67, 0xc1, 0xc5, 0x0a, 0xe4, 0x25, 0x9a, 0x8c, 0x40, 0x23, 0x9f, 0xd0, 0xff, 0xa1, 0x34, + 0x26, 0xcc, 0xf5, 0x1c, 0xc3, 0x27, 0x3f, 0xe2, 0x40, 0x88, 0x96, 0xd3, 0x8b, 0xe1, 0x5a, 0x8b, + 0x2f, 0x9d, 0x42, 0x45, 0xee, 0x9d, 0xa9, 0x58, 0x3c, 0x81, 0x8a, 0x67, 0x79, 0x28, 0x37, 0xf6, + 0xb7, 0x9b, 0x78, 0x80, 0x1d, 0x93, 0xbd, 0xea, 0x25, 0x65, 0x0e, 0x2f, 0x65, 0xce, 0xd0, 0x4b, + 0xd9, 0xf7, 0xf1, 0xd2, 0x77, 0x70, 0xbe, 0xeb, 0x1b, 0x21, 0x1a, 0x63, 0xe0, 0x52, 0x4e, 0x5c, + 0x76, 0x0e, 0x48, 0xc5, 0xae, 0xdf, 0xe0, 0xa0, 0x1e, 0xbb, 0x54, 0x08, 0x48, 0x99, 0x19, 0xb0, + 0x59, 0x86, 0x8b, 0x62, 0x4d, 0x4a, 0xf1, 0x3f, 0x00, 0xec, 0xd9, 0xb3, 0xfe, 0x2d, 0x60, 0xcf, + 0x96, 0xe1, 0x55, 0x28, 0x30, 0xc2, 0xcc, 0x81, 0x41, 0xcd, 0xc8, 0xab, 0xe7, 0xc4, 0x42, 0xdb, + 0x14, 0xb5, 0x72, 0x83, 0x06, 0x9b, 0xa8, 0xe7, 0x38, 0x95, 0x7a, 0x41, 0xae, 0xec, 0x4f, 0x84, + 0xca, 0x32, 0x4c, 0x46, 0xcc, 0x1f, 0x31, 0xc3, 0xb5, 0x27, 0x6a, 0x61, 0x43, 0xa9, 0x95, 0xf5, + 0x8a, 0x8c, 0x3c, 0x15, 0x81, 0x5d, 0x7b, 0x82, 0xb6, 0xa0, 0x28, 0x94, 0x97, 0xdd, 0x40, 0x08, + 0x73, 0xf1, 0xf0, 0x68, 0x9d, 0x6b, 0xdf, 0x96, 0x91, 0xfd, 0x89, 0x0e, 0x34, 0xfe, 0x8d, 0xbe, + 0x87, 0xb2, 0x1d, 0xba, 0x82, 0x04, 0x06, 0x75, 0x1d, 0xb5, 0x28, 0xaa, 0x3e, 0x3d, 0x3c, 0x5a, + 0xbf, 0xfb, 0x2e, 0xdc, 0xb5, 0x5d, 0xc7, 0x33, 0xd9, 0x28, 0xc0, 0x7a, 0x29, 0xee, 0xd7, 0x76, + 0x1d, 0x74, 0x00, 0x65, 0x8b, 0x8c, 0xb1, 0x67, 0x7a, 0x8c, 0xb7, 0xa7, 0x6a, 0x69, 0x23, 0x5b, + 0x2b, 0x6e, 0xdd, 0x3a, 0x41, 0xe2, 0x6d, 0x99, 0x7b, 0xdf, 0x36, 0xfd, 0xb0, 0x43, 0xd8, 0x95, + 0xea, 0xa5, 0xa8, 0x4d, 0xdb, 0x75, 0x28, 0xfa, 0x10, 0xce, 0x8f, 0xbc, 0x0e, 0xf1, 0x6c, 0xb1, + 0x57, 0x77, 0x88, 0xd5, 0xb2, 0x20, 0xa5, 0x1c, 0xaf, 0xee, 0xbb, 0x43, 0x8c, 0xbe, 0x84, 0x0a, + 0xf7, 0xc5, 0xc8, 0xb3, 0x63, 0xe7, 0xab, 0xe7, 0x85, 0xc7, 0x6e, 0x9c, 0x00, 0xa0, 0xb1, 0xbf, + 0x7d, 0x90, 0xca, 0xd6, 0x2f, 0x74, 0x98, 0x95, 0x5e, 0xa8, 0xfe, 0x96, 0x83, 0x0b, 0xc7, 0x92, + 0xb8, 0x49, 0x52, 0x68, 0x26, 0xe1, 0x27, 0x45, 0x2f, 0x26, 0x58, 0x5e, 0xd1, 0x26, 0xf3, 0x36, + 0xda, 0xfc, 0x00, 0x57, 0x12, 0x6d, 0x92, 0x01, 0x5c, 0xa5, 0xec, 0xbc, 0x2a, 0x5d, 0x8e, 0x3b, + 0x1f, 0x44, 0x8d, 0xb9, 0x5c, 0x04, 0x56, 0x52, 0x76, 0x88, 0x00, 0xf3, 0x89, 0xb9, 0x79, 0x27, + 0x2e, 0x27, 0xbe, 0x90, 0x7d, 0xf9, 0xc0, 0x2e, 0xac, 0x24, 0xfe, 0x48, 0xcd, 0xa3, 0xea, 0xe2, + 0x7b, 0x1a, 0x65, 0x39, 0x36, 0x4a, 0x32, 0x86, 0x22, 0x0b, 0x56, 0xe3, 0x39, 0x33, 0x54, 0x86, + 0x5f, 0x8c, 0xbc, 0x18, 0x76, 0xfd, 0x84, 0x61, 0x71, 0xf7, 0x5d, 0xaf, 0x4b, 0x74, 0x35, 0x6a, + 0x94, 0x66, 0x8e, 0x7f, 0x2c, 0xaa, 0x3f, 0x65, 0xe0, 0xd2, 0x31, 0x6f, 0xf0, 0x8a, 0xb7, 0xf1, + 0xc7, 0x1b, 0xf0, 0x65, 0xce, 0x02, 0xdf, 0x29, 0x64, 0x67, 0xcf, 0x92, 0xec, 0x6a, 0x1b, 0xae, + 0x24, 0xa7, 0x0d, 0x09, 0x92, 0x63, 0x87, 0xa2, 0x4f, 0x20, 0x67, 0xe3, 0x01, 0x55, 0x95, 0x53, + 0x37, 0x34, 0x73, 0x56, 0xe9, 0xa2, 0xa2, 0xba, 0x07, 0xab, 0xaf, 0x6f, 0xba, 0xeb, 0xd9, 0x78, + 0x82, 0xea, 0xb0, 0x9c, 0x7c, 0x49, 0x8d, 0x9e, 0x49, 0x7b, 0x21, 0x73, 0x7c, 0x50, 0x49, 0xbf, + 0x18, 0x7f, 0x53, 0x1f, 0x9a, 0xb4, 0x27, 0xc4, 0xfa, 0x5d, 0x81, 0xf2, 0x0c, 0x71, 0xe8, 0x01, + 0x64, 0xe6, 0xbe, 0x0f, 0x64, 0xfc, 0x3e, 0x7a, 0x04, 0x59, 0xfe, 0xc6, 0x64, 0xe6, 0x7d, 0x63, + 0x78, 0x97, 0xea, 0x2f, 0x0a, 0x5c, 0x3d, 0x91, 0x7f, 0x7e, 0x0c, 0x5b, 0x64, 0x7c, 0x06, 0xd7, + 0x18, 0x8b, 0x8c, 0x5b, 0x7d, 0x6e, 0x54, 0x33, 0x9c, 0x11, 0xda, 0x22, 0x23, 0xc8, 0x2b, 0x9a, + 0xf1, 0x5c, 0x5a, 0xfd, 0x53, 0x81, 0xab, 0x6d, 0x3c, 0xc0, 0x16, 0x73, 0xc7, 0x38, 0x52, 0x7d, + 0x87, 0x5f, 0xae, 0x3c, 0x0b, 0xa3, 0x1b, 0x70, 0xe1, 0x98, 0x0a, 0x02, 0x58, 0x41, 0x2f, 0xcf, + 0x08, 0x80, 0x74, 0x28, 0xc4, 0x67, 0xf6, 0x9c, 0x37, 0x88, 0x25, 0x79, 0x5c, 0xa3, 0x9b, 0x70, + 0x29, 0xc0, 0xdc, 0x8f, 0x01, 0xb6, 0x0d, 0xd9, 0x9d, 0x86, 0xf7, 0xe4, 0x92, 0x5e, 0x89, 0x43, + 0x0f, 0x78, 0x7a, 0xbb, 0xff, 0xd1, 0x8e, 0x78, 0x57, 0x13, 0x1b, 0xb5, 0x99, 0xc9, 0x46, 0x14, + 0x15, 0x61, 0xa9, 0xb5, 0xb3, 0xd7, 0xdc, 0xdd, 0xfb, 0xa2, 0xb2, 0x80, 0x00, 0xf2, 0xf7, 0xb7, + 0xf7, 0x77, 0xbf, 0xda, 0xa9, 0x28, 0xa8, 0x04, 0xe7, 0x0e, 0xf6, 0x1a, 0x4f, 0xf7, 0x9a, 0x3b, + 0xcd, 0x4a, 0x06, 0x2d, 0x41, 0xf6, 0xfe, 0xde, 0x37, 0x95, 0x6c, 0xe3, 0xf1, 0xb3, 0x17, 0x6b, + 0xca, 0xf3, 0x17, 0x6b, 0xca, 0xbf, 0x2f, 0xd6, 0x94, 0x5f, 0x5f, 0xae, 0x2d, 0x3c, 0x7f, 0xb9, + 0xb6, 0xf0, 0xcf, 0xcb, 0xb5, 0x85, 0x6f, 0xdf, 0xb8, 0x99, 0x49, 0xfa, 0x4f, 0x89, 0xd8, 0x59, + 0x27, 0x2f, 0xfe, 0x94, 0xdc, 0xfe, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x72, 0x85, 0x4f, 0x14, 0x71, + 0x0d, 0x00, 0x00, +} + +func (m *FinalityProvider) 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 *FinalityProvider) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FinalityProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SlashedBtcHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.SlashedBtcHeight)) + i-- + dAtA[i] = 0x38 + } + if m.SlashedBabylonHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.SlashedBabylonHeight)) + i-- + dAtA[i] = 0x30 + } + if m.Pop != nil { + { + size, err := m.Pop.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Commission != nil { + { + size := m.Commission.Size() + i -= size + if _, err := m.Commission.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Description != nil { + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FinalityProviderWithMeta) 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 *FinalityProviderWithMeta) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FinalityProviderWithMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SlashedBtcHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.SlashedBtcHeight)) + i-- + dAtA[i] = 0x28 + } + if m.SlashedBabylonHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.SlashedBabylonHeight)) + i-- + dAtA[i] = 0x20 + } + if m.VotingPower != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x18 + } + if m.Height != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BTCDelegation) 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 *BTCDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BtcUndelegation != nil { + { + size, err := m.BtcUndelegation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x72 + } + if m.UnbondingTime != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.UnbondingTime)) + i-- + dAtA[i] = 0x68 + } + if len(m.CovenantSigs) > 0 { + for iNdEx := len(m.CovenantSigs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantSigs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + } + if m.DelegatorSig != nil { + { + size := m.DelegatorSig.Size() + i -= size + if _, err := m.DelegatorSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + if m.SlashingTx != nil { + { + size := m.SlashingTx.Size() + i -= size + if _, err := m.SlashingTx.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + if m.StakingOutputIdx != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.StakingOutputIdx)) + i-- + dAtA[i] = 0x48 + } + if len(m.StakingTx) > 0 { + i -= len(m.StakingTx) + copy(dAtA[i:], m.StakingTx) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.StakingTx))) + i-- + dAtA[i] = 0x42 + } + if m.TotalSat != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.TotalSat)) + i-- + dAtA[i] = 0x38 + } + if m.EndHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.EndHeight)) + i-- + dAtA[i] = 0x30 + } + if m.StartHeight != 0 { + i = encodeVarintBtcstaking(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x28 + } + if len(m.FpBtcPkList) > 0 { + for iNdEx := len(m.FpBtcPkList) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.FpBtcPkList[iNdEx].Size() + i -= size + if _, err := m.FpBtcPkList[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.Pop != nil { + { + size, err := m.Pop.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BTCUndelegation) 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 *BTCUndelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCUndelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CovenantUnbondingSigList) > 0 { + for iNdEx := len(m.CovenantUnbondingSigList) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantUnbondingSigList[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.CovenantSlashingSigs) > 0 { + for iNdEx := len(m.CovenantSlashingSigs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantSlashingSigs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.DelegatorSlashingSig != nil { + { + size := m.DelegatorSlashingSig.Size() + i -= size + if _, err := m.DelegatorSlashingSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.DelegatorUnbondingSig != nil { + { + size := m.DelegatorUnbondingSig.Size() + i -= size + if _, err := m.DelegatorUnbondingSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.SlashingTx != nil { + { + size := m.SlashingTx.Size() + i -= size + if _, err := m.SlashingTx.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.UnbondingTx) > 0 { + i -= len(m.UnbondingTx) + copy(dAtA[i:], m.UnbondingTx) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.UnbondingTx))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BTCUndelegationInfo) 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 *BTCUndelegationInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCUndelegationInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CovenantSlashingSigs) > 0 { + for iNdEx := len(m.CovenantSlashingSigs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantSlashingSigs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.CovenantUnbondingSigList) > 0 { + for iNdEx := len(m.CovenantUnbondingSigList) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantUnbondingSigList[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.UnbondingTx) > 0 { + i -= len(m.UnbondingTx) + copy(dAtA[i:], m.UnbondingTx) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.UnbondingTx))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BTCDelegatorDelegations) 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 *BTCDelegatorDelegations) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCDelegatorDelegations) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Dels) > 0 { + for iNdEx := len(m.Dels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Dels[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *BTCDelegatorDelegationIndex) 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 *BTCDelegatorDelegationIndex) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCDelegatorDelegationIndex) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StakingTxHashList) > 0 { + for iNdEx := len(m.StakingTxHashList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StakingTxHashList[iNdEx]) + copy(dAtA[i:], m.StakingTxHashList[iNdEx]) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.StakingTxHashList[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SignatureInfo) 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 *SignatureInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sig != nil { + { + size := m.Sig.Size() + i -= size + if _, err := m.Sig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Pk != nil { + { + size := m.Pk.Size() + i -= size + if _, err := m.Pk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CovenantAdaptorSignatures) 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 *CovenantAdaptorSignatures) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CovenantAdaptorSignatures) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AdaptorSigs) > 0 { + for iNdEx := len(m.AdaptorSigs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AdaptorSigs[iNdEx]) + copy(dAtA[i:], m.AdaptorSigs[iNdEx]) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.AdaptorSigs[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.CovPk != nil { + { + size := m.CovPk.Size() + i -= size + if _, err := m.CovPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SelectiveSlashingEvidence) 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 *SelectiveSlashingEvidence) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectiveSlashingEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RecoveredFpBtcSk) > 0 { + i -= len(m.RecoveredFpBtcSk) + copy(dAtA[i:], m.RecoveredFpBtcSk) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.RecoveredFpBtcSk))) + i-- + dAtA[i] = 0x1a + } + if m.FpBtcPk != nil { + { + size := m.FpBtcPk.Size() + i -= size + if _, err := m.FpBtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBtcstaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.StakingTxHash) > 0 { + i -= len(m.StakingTxHash) + copy(dAtA[i:], m.StakingTxHash) + i = encodeVarintBtcstaking(dAtA, i, uint64(len(m.StakingTxHash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintBtcstaking(dAtA []byte, offset int, v uint64) int { + offset -= sovBtcstaking(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *FinalityProvider) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Description != nil { + l = m.Description.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.Commission != nil { + l = m.Commission.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.Pop != nil { + l = m.Pop.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.SlashedBabylonHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.SlashedBabylonHeight)) + } + if m.SlashedBtcHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.SlashedBtcHeight)) + } + return n +} + +func (m *FinalityProviderWithMeta) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovBtcstaking(uint64(m.Height)) + } + if m.VotingPower != 0 { + n += 1 + sovBtcstaking(uint64(m.VotingPower)) + } + if m.SlashedBabylonHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.SlashedBabylonHeight)) + } + if m.SlashedBtcHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.SlashedBtcHeight)) + } + return n +} + +func (m *BTCDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.Pop != nil { + l = m.Pop.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if len(m.FpBtcPkList) > 0 { + for _, e := range m.FpBtcPkList { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + if m.StartHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.StartHeight)) + } + if m.EndHeight != 0 { + n += 1 + sovBtcstaking(uint64(m.EndHeight)) + } + if m.TotalSat != 0 { + n += 1 + sovBtcstaking(uint64(m.TotalSat)) + } + l = len(m.StakingTx) + if l > 0 { + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.StakingOutputIdx != 0 { + n += 1 + sovBtcstaking(uint64(m.StakingOutputIdx)) + } + if m.SlashingTx != nil { + l = m.SlashingTx.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.DelegatorSig != nil { + l = m.DelegatorSig.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if len(m.CovenantSigs) > 0 { + for _, e := range m.CovenantSigs { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + if m.UnbondingTime != 0 { + n += 1 + sovBtcstaking(uint64(m.UnbondingTime)) + } + if m.BtcUndelegation != nil { + l = m.BtcUndelegation.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + return n +} + +func (m *BTCUndelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UnbondingTx) + if l > 0 { + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.SlashingTx != nil { + l = m.SlashingTx.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.DelegatorUnbondingSig != nil { + l = m.DelegatorUnbondingSig.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.DelegatorSlashingSig != nil { + l = m.DelegatorSlashingSig.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if len(m.CovenantSlashingSigs) > 0 { + for _, e := range m.CovenantSlashingSigs { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + if len(m.CovenantUnbondingSigList) > 0 { + for _, e := range m.CovenantUnbondingSigList { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + return n +} + +func (m *BTCUndelegationInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UnbondingTx) + if l > 0 { + n += 1 + l + sovBtcstaking(uint64(l)) + } + if len(m.CovenantUnbondingSigList) > 0 { + for _, e := range m.CovenantUnbondingSigList { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + if len(m.CovenantSlashingSigs) > 0 { + for _, e := range m.CovenantSlashingSigs { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + return n +} + +func (m *BTCDelegatorDelegations) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Dels) > 0 { + for _, e := range m.Dels { + l = e.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + return n +} + +func (m *BTCDelegatorDelegationIndex) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.StakingTxHashList) > 0 { + for _, b := range m.StakingTxHashList { + l = len(b) + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + return n +} + +func (m *SignatureInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pk != nil { + l = m.Pk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.Sig != nil { + l = m.Sig.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + return n +} + +func (m *CovenantAdaptorSignatures) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CovPk != nil { + l = m.CovPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + if len(m.AdaptorSigs) > 0 { + for _, b := range m.AdaptorSigs { + l = len(b) + n += 1 + l + sovBtcstaking(uint64(l)) + } + } + return n +} + +func (m *SelectiveSlashingEvidence) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.StakingTxHash) + if l > 0 { + n += 1 + l + sovBtcstaking(uint64(l)) + } + if m.FpBtcPk != nil { + l = m.FpBtcPk.Size() + n += 1 + l + sovBtcstaking(uint64(l)) + } + l = len(m.RecoveredFpBtcSk) + if l > 0 { + n += 1 + l + sovBtcstaking(uint64(l)) + } + return n +} + +func sovBtcstaking(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozBtcstaking(x uint64) (n int) { + return sovBtcstaking(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FinalityProvider) 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 ErrIntOverflowBtcstaking + } + 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: FinalityProvider: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FinalityProvider: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Description == nil { + m.Description = &types.Description{} + } + if err := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + 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 ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.LegacyDec + m.Commission = &v + if err := m.Commission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pop", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pop == nil { + m.Pop = &ProofOfPossession{} + } + if err := m.Pop.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashedBabylonHeight", wireType) + } + m.SlashedBabylonHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlashedBabylonHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashedBtcHeight", wireType) + } + m.SlashedBtcHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlashedBtcHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FinalityProviderWithMeta) 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 ErrIntOverflowBtcstaking + } + 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: FinalityProviderWithMeta: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FinalityProviderWithMeta: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashedBabylonHeight", wireType) + } + m.SlashedBabylonHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlashedBabylonHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashedBtcHeight", wireType) + } + m.SlashedBtcHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlashedBtcHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCDelegation) 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 ErrIntOverflowBtcstaking + } + 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: BTCDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pop", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pop == nil { + m.Pop = &ProofOfPossession{} + } + if err := m.Pop.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPkList = append(m.FpBtcPkList, v) + if err := m.FpBtcPkList[len(m.FpBtcPkList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EndHeight", wireType) + } + m.EndHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EndHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSat", wireType) + } + m.TotalSat = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalSat |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTx = append(m.StakingTx[:0], dAtA[iNdEx:postIndex]...) + if m.StakingTx == nil { + m.StakingTx = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingOutputIdx", wireType) + } + m.StakingOutputIdx = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StakingOutputIdx |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v BTCSlashingTx + m.SlashingTx = &v + if err := m.SlashingTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.DelegatorSig = &v + if err := m.DelegatorSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantSigs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CovenantSigs = append(m.CovenantSigs, &CovenantAdaptorSignatures{}) + if err := m.CovenantSigs[len(m.CovenantSigs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + m.UnbondingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingTime |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcUndelegation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BtcUndelegation == nil { + m.BtcUndelegation = &BTCUndelegation{} + } + if err := m.BtcUndelegation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCUndelegation) 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 ErrIntOverflowBtcstaking + } + 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: BTCUndelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCUndelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnbondingTx = append(m.UnbondingTx[:0], dAtA[iNdEx:postIndex]...) + if m.UnbondingTx == nil { + m.UnbondingTx = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v BTCSlashingTx + m.SlashingTx = &v + if err := m.SlashingTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorUnbondingSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.DelegatorUnbondingSig = &v + if err := m.DelegatorUnbondingSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorSlashingSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.DelegatorSlashingSig = &v + if err := m.DelegatorSlashingSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantSlashingSigs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CovenantSlashingSigs = append(m.CovenantSlashingSigs, &CovenantAdaptorSignatures{}) + if err := m.CovenantSlashingSigs[len(m.CovenantSlashingSigs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantUnbondingSigList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CovenantUnbondingSigList = append(m.CovenantUnbondingSigList, &SignatureInfo{}) + if err := m.CovenantUnbondingSigList[len(m.CovenantUnbondingSigList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCUndelegationInfo) 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 ErrIntOverflowBtcstaking + } + 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: BTCUndelegationInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCUndelegationInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnbondingTx = append(m.UnbondingTx[:0], dAtA[iNdEx:postIndex]...) + if m.UnbondingTx == nil { + m.UnbondingTx = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantUnbondingSigList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CovenantUnbondingSigList = append(m.CovenantUnbondingSigList, &SignatureInfo{}) + if err := m.CovenantUnbondingSigList[len(m.CovenantUnbondingSigList)-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 CovenantSlashingSigs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CovenantSlashingSigs = append(m.CovenantSlashingSigs, &CovenantAdaptorSignatures{}) + if err := m.CovenantSlashingSigs[len(m.CovenantSlashingSigs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCDelegatorDelegations) 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 ErrIntOverflowBtcstaking + } + 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: BTCDelegatorDelegations: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCDelegatorDelegations: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Dels = append(m.Dels, &BTCDelegation{}) + if err := m.Dels[len(m.Dels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCDelegatorDelegationIndex) 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 ErrIntOverflowBtcstaking + } + 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: BTCDelegatorDelegationIndex: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCDelegatorDelegationIndex: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHashList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTxHashList = append(m.StakingTxHashList, make([]byte, postIndex-iNdEx)) + copy(m.StakingTxHashList[len(m.StakingTxHashList)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureInfo) 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 ErrIntOverflowBtcstaking + } + 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: SignatureInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.Pk = &v + if err := m.Pk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.Sig = &v + if err := m.Sig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CovenantAdaptorSignatures) 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 ErrIntOverflowBtcstaking + } + 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: CovenantAdaptorSignatures: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CovenantAdaptorSignatures: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.CovPk = &v + if err := m.CovPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdaptorSigs", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdaptorSigs = append(m.AdaptorSigs, make([]byte, postIndex-iNdEx)) + copy(m.AdaptorSigs[len(m.AdaptorSigs)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SelectiveSlashingEvidence) 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 ErrIntOverflowBtcstaking + } + 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: SelectiveSlashingEvidence: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SelectiveSlashingEvidence: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + 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 ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPk = &v + if err := m.FpBtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecoveredFpBtcSk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtcstaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBtcstaking + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBtcstaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RecoveredFpBtcSk = append(m.RecoveredFpBtcSk[:0], dAtA[iNdEx:postIndex]...) + if m.RecoveredFpBtcSk == nil { + m.RecoveredFpBtcSk = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBtcstaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBtcstaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipBtcstaking(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, ErrIntOverflowBtcstaking + } + 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, ErrIntOverflowBtcstaking + } + 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, ErrIntOverflowBtcstaking + } + 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, ErrInvalidLengthBtcstaking + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupBtcstaking + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthBtcstaking + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthBtcstaking = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowBtcstaking = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupBtcstaking = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/codec.go b/x/btcstaking/types/codec.go new file mode 100644 index 000000000..256b38291 --- /dev/null +++ b/x/btcstaking/types/codec.go @@ -0,0 +1,33 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgCreateFinalityProvider{}, "btcstaking/MsgCreateFinalityProvider", nil) + cdc.RegisterConcrete(&MsgCreateBTCDelegation{}, "btcstaking/MsgCreateBTCDelegation", nil) + cdc.RegisterConcrete(&MsgAddCovenantSigs{}, "btcstaking/MsgAddCovenantSigs", nil) + cdc.RegisterConcrete(&MsgUpdateParams{}, "btcstaking/MsgUpdateParams", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + // Register messages + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgCreateFinalityProvider{}, + &MsgCreateBTCDelegation{}, + &MsgAddCovenantSigs{}, + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/btcstaking/types/errors.go b/x/btcstaking/types/errors.go new file mode 100644 index 000000000..8f65379e5 --- /dev/null +++ b/x/btcstaking/types/errors.go @@ -0,0 +1,30 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +// x/btcstaking module sentinel errors +var ( + ErrFpNotFound = errorsmod.Register(ModuleName, 1100, "the finality provider is not found") + ErrBTCDelegatorNotFound = errorsmod.Register(ModuleName, 1101, "the BTC delegator is not found") + ErrBTCDelegationNotFound = errorsmod.Register(ModuleName, 1102, "the BTC delegation is not found") + ErrFpRegistered = errorsmod.Register(ModuleName, 1103, "the finality provider has already been registered") + ErrFpAlreadySlashed = errorsmod.Register(ModuleName, 1104, "the finality provider has already been slashed") + ErrBTCStakingNotActivated = errorsmod.Register(ModuleName, 1105, "the BTC staking protocol is not activated yet") + ErrBTCHeightNotFound = errorsmod.Register(ModuleName, 1106, "the BTC height is not found") + ErrReusedStakingTx = errorsmod.Register(ModuleName, 1107, "the BTC staking tx is already used") + ErrInvalidCovenantPK = errorsmod.Register(ModuleName, 1108, "the BTC staking tx specifies a wrong covenant PK") + ErrInvalidStakingTx = errorsmod.Register(ModuleName, 1109, "the BTC staking tx is not valid") + ErrInvalidSlashingTx = errorsmod.Register(ModuleName, 1110, "the BTC slashing tx is not valid") + ErrInvalidCovenantSig = errorsmod.Register(ModuleName, 1111, "the covenant signature is not valid") + ErrCommissionLTMinRate = errorsmod.Register(ModuleName, 1112, "commission cannot be less than min rate") + ErrCommissionGTMaxRate = errorsmod.Register(ModuleName, 1113, "commission cannot be more than one") + ErrInvalidDelegationState = errorsmod.Register(ModuleName, 1114, "Unexpected delegation state") + ErrInvalidUnbondingTx = errorsmod.Register(ModuleName, 1115, "the BTC unbonding tx is not valid") + ErrRewardDistCacheNotFound = errorsmod.Register(ModuleName, 1116, "the reward distribution cache is not found") + ErrEmptyFpList = errorsmod.Register(ModuleName, 1117, "the finality provider list is empty") + ErrInvalidProofOfPossession = errorsmod.Register(ModuleName, 1118, "the proof of possession is not valid") + ErrDuplicatedFp = errorsmod.Register(ModuleName, 1119, "the staking request contains duplicated finality provider public key") + ErrInvalidBTCUndelegateReq = errorsmod.Register(ModuleName, 1120, "invalid undelegation request") +) diff --git a/x/btcstaking/types/events.pb.go b/x/btcstaking/types/events.pb.go new file mode 100644 index 000000000..d4c7a690d --- /dev/null +++ b/x/btcstaking/types/events.pb.go @@ -0,0 +1,1270 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/events.proto + +package types + +import ( + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "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 + +// EventNewFinalityProvider is the event emitted when a finality provider is created +type EventNewFinalityProvider struct { + Fp *FinalityProvider `protobuf:"bytes,1,opt,name=fp,proto3" json:"fp,omitempty"` +} + +func (m *EventNewFinalityProvider) Reset() { *m = EventNewFinalityProvider{} } +func (m *EventNewFinalityProvider) String() string { return proto.CompactTextString(m) } +func (*EventNewFinalityProvider) ProtoMessage() {} +func (*EventNewFinalityProvider) Descriptor() ([]byte, []int) { + return fileDescriptor_74118427820fff75, []int{0} +} +func (m *EventNewFinalityProvider) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventNewFinalityProvider) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventNewFinalityProvider.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 *EventNewFinalityProvider) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventNewFinalityProvider.Merge(m, src) +} +func (m *EventNewFinalityProvider) XXX_Size() int { + return m.Size() +} +func (m *EventNewFinalityProvider) XXX_DiscardUnknown() { + xxx_messageInfo_EventNewFinalityProvider.DiscardUnknown(m) +} + +var xxx_messageInfo_EventNewFinalityProvider proto.InternalMessageInfo + +func (m *EventNewFinalityProvider) GetFp() *FinalityProvider { + if m != nil { + return m.Fp + } + return nil +} + +// EventNewBTCDelegation is the event emitted when a BTC delegation is created +// NOTE: the BTC delegation is not active thus does not have voting power yet +// only after it receives a covenant signature it becomes activated and has voting power +type EventNewBTCDelegation struct { + BtcDel *BTCDelegation `protobuf:"bytes,1,opt,name=btc_del,json=btcDel,proto3" json:"btc_del,omitempty"` +} + +func (m *EventNewBTCDelegation) Reset() { *m = EventNewBTCDelegation{} } +func (m *EventNewBTCDelegation) String() string { return proto.CompactTextString(m) } +func (*EventNewBTCDelegation) ProtoMessage() {} +func (*EventNewBTCDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_74118427820fff75, []int{1} +} +func (m *EventNewBTCDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventNewBTCDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventNewBTCDelegation.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 *EventNewBTCDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventNewBTCDelegation.Merge(m, src) +} +func (m *EventNewBTCDelegation) XXX_Size() int { + return m.Size() +} +func (m *EventNewBTCDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_EventNewBTCDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_EventNewBTCDelegation proto.InternalMessageInfo + +func (m *EventNewBTCDelegation) GetBtcDel() *BTCDelegation { + if m != nil { + return m.BtcDel + } + return nil +} + +// EventActivateBTCDelegation is the event emitted when covenant activates a BTC delegation +// such that the BTC delegation starts to have voting power in its timelock period +type EventActivateBTCDelegation struct { + BtcDel *BTCDelegation `protobuf:"bytes,1,opt,name=btc_del,json=btcDel,proto3" json:"btc_del,omitempty"` +} + +func (m *EventActivateBTCDelegation) Reset() { *m = EventActivateBTCDelegation{} } +func (m *EventActivateBTCDelegation) String() string { return proto.CompactTextString(m) } +func (*EventActivateBTCDelegation) ProtoMessage() {} +func (*EventActivateBTCDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_74118427820fff75, []int{2} +} +func (m *EventActivateBTCDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventActivateBTCDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventActivateBTCDelegation.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 *EventActivateBTCDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventActivateBTCDelegation.Merge(m, src) +} +func (m *EventActivateBTCDelegation) XXX_Size() int { + return m.Size() +} +func (m *EventActivateBTCDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_EventActivateBTCDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_EventActivateBTCDelegation proto.InternalMessageInfo + +func (m *EventActivateBTCDelegation) GetBtcDel() *BTCDelegation { + if m != nil { + return m.BtcDel + } + return nil +} + +// EventUnbondingBTCDelegation is the event emitted when an unbonding BTC delegation +// receives all signatures needed for becoming unbonded +type EventUnbondedBTCDelegation struct { + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + // If there is more than 1 PKs, then this means the delegation is restaked + // to multiple finality providers + FpBtcPkList []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,rep,name=fp_btc_pk_list,json=fpBtcPkList,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk_list,omitempty"` + // staking_tx_hash is the hash of the staking tx. + // (fp_pks..., del_pk, staking_tx_hash) uniquely identifies a BTC delegation + StakingTxHash string `protobuf:"bytes,3,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"` + // unbonding_tx_hash is the hash of the unbonding tx. + UnbondingTxHash string `protobuf:"bytes,4,opt,name=unbonding_tx_hash,json=unbondingTxHash,proto3" json:"unbonding_tx_hash,omitempty"` + // from_state is the last state the BTC delegation was at + FromState BTCDelegationStatus `protobuf:"varint,5,opt,name=from_state,json=fromState,proto3,enum=babylon.btcstaking.v1.BTCDelegationStatus" json:"from_state,omitempty"` +} + +func (m *EventUnbondedBTCDelegation) Reset() { *m = EventUnbondedBTCDelegation{} } +func (m *EventUnbondedBTCDelegation) String() string { return proto.CompactTextString(m) } +func (*EventUnbondedBTCDelegation) ProtoMessage() {} +func (*EventUnbondedBTCDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_74118427820fff75, []int{3} +} +func (m *EventUnbondedBTCDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventUnbondedBTCDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventUnbondedBTCDelegation.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 *EventUnbondedBTCDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventUnbondedBTCDelegation.Merge(m, src) +} +func (m *EventUnbondedBTCDelegation) XXX_Size() int { + return m.Size() +} +func (m *EventUnbondedBTCDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_EventUnbondedBTCDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_EventUnbondedBTCDelegation proto.InternalMessageInfo + +func (m *EventUnbondedBTCDelegation) GetStakingTxHash() string { + if m != nil { + return m.StakingTxHash + } + return "" +} + +func (m *EventUnbondedBTCDelegation) GetUnbondingTxHash() string { + if m != nil { + return m.UnbondingTxHash + } + return "" +} + +func (m *EventUnbondedBTCDelegation) GetFromState() BTCDelegationStatus { + if m != nil { + return m.FromState + } + return BTCDelegationStatus_PENDING +} + +// EventSelectiveSlashing is the event emitted when an adversarial +// finality provider selectively slashes a BTC delegation. This will +// result in slashing of all BTC delegations under this finality provider. +type EventSelectiveSlashing struct { + // evidence is the evidence of selective slashing + Evidence *SelectiveSlashingEvidence `protobuf:"bytes,1,opt,name=evidence,proto3" json:"evidence,omitempty"` +} + +func (m *EventSelectiveSlashing) Reset() { *m = EventSelectiveSlashing{} } +func (m *EventSelectiveSlashing) String() string { return proto.CompactTextString(m) } +func (*EventSelectiveSlashing) ProtoMessage() {} +func (*EventSelectiveSlashing) Descriptor() ([]byte, []int) { + return fileDescriptor_74118427820fff75, []int{4} +} +func (m *EventSelectiveSlashing) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventSelectiveSlashing) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventSelectiveSlashing.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 *EventSelectiveSlashing) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventSelectiveSlashing.Merge(m, src) +} +func (m *EventSelectiveSlashing) XXX_Size() int { + return m.Size() +} +func (m *EventSelectiveSlashing) XXX_DiscardUnknown() { + xxx_messageInfo_EventSelectiveSlashing.DiscardUnknown(m) +} + +var xxx_messageInfo_EventSelectiveSlashing proto.InternalMessageInfo + +func (m *EventSelectiveSlashing) GetEvidence() *SelectiveSlashingEvidence { + if m != nil { + return m.Evidence + } + return nil +} + +func init() { + proto.RegisterType((*EventNewFinalityProvider)(nil), "babylon.btcstaking.v1.EventNewFinalityProvider") + proto.RegisterType((*EventNewBTCDelegation)(nil), "babylon.btcstaking.v1.EventNewBTCDelegation") + proto.RegisterType((*EventActivateBTCDelegation)(nil), "babylon.btcstaking.v1.EventActivateBTCDelegation") + proto.RegisterType((*EventUnbondedBTCDelegation)(nil), "babylon.btcstaking.v1.EventUnbondedBTCDelegation") + proto.RegisterType((*EventSelectiveSlashing)(nil), "babylon.btcstaking.v1.EventSelectiveSlashing") +} + +func init() { + proto.RegisterFile("babylon/btcstaking/v1/events.proto", fileDescriptor_74118427820fff75) +} + +var fileDescriptor_74118427820fff75 = []byte{ + // 469 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x41, 0x6f, 0xd3, 0x30, + 0x18, 0x86, 0x9b, 0x8e, 0x0d, 0xe6, 0xc1, 0x26, 0x22, 0x86, 0xa2, 0x1e, 0x42, 0x15, 0xa1, 0x51, + 0xed, 0x90, 0x6c, 0x1d, 0x82, 0x13, 0x07, 0xc2, 0x86, 0x98, 0x28, 0xa8, 0x4a, 0x07, 0x07, 0x76, + 0x88, 0xec, 0xf4, 0x4b, 0x62, 0x35, 0xb3, 0xa3, 0xfa, 0x6b, 0x68, 0xff, 0x05, 0x3f, 0x8b, 0xe3, + 0x8e, 0x88, 0x03, 0x42, 0xed, 0x81, 0xbf, 0x81, 0x92, 0x79, 0xa3, 0x83, 0x55, 0x20, 0xb1, 0x9b, + 0x93, 0x3c, 0xef, 0xf3, 0xc9, 0xaf, 0x63, 0xe2, 0x30, 0xca, 0x26, 0x99, 0x14, 0x1e, 0xc3, 0x48, + 0x21, 0x1d, 0x70, 0x91, 0x78, 0xc5, 0xae, 0x07, 0x05, 0x08, 0x54, 0x6e, 0x3e, 0x94, 0x28, 0xcd, + 0x4d, 0xcd, 0xb8, 0xbf, 0x18, 0xb7, 0xd8, 0x6d, 0xdc, 0x4b, 0x64, 0x22, 0x2b, 0xc2, 0x2b, 0x57, + 0x67, 0x70, 0x63, 0xeb, 0x6a, 0xe1, 0x5c, 0xb4, 0xe2, 0x9c, 0x1e, 0xb1, 0x0e, 0xca, 0x21, 0x6f, + 0xe1, 0xe3, 0x4b, 0x2e, 0x68, 0xc6, 0x71, 0xd2, 0x1d, 0xca, 0x82, 0xf7, 0x61, 0x68, 0x3e, 0x25, + 0xf5, 0x38, 0xb7, 0x8c, 0xa6, 0xd1, 0x5a, 0x6b, 0x3f, 0x72, 0xaf, 0x9c, 0xee, 0xfe, 0x1e, 0x0a, + 0xea, 0x71, 0xee, 0xbc, 0x27, 0x9b, 0xe7, 0x52, 0xff, 0xe8, 0xc5, 0x3e, 0x64, 0x90, 0x50, 0xe4, + 0x52, 0x98, 0xcf, 0xc8, 0x4d, 0x86, 0x51, 0xd8, 0x87, 0x4c, 0x6b, 0x1f, 0x2e, 0xd0, 0x5e, 0x8a, + 0x05, 0x2b, 0x0c, 0xa3, 0x7d, 0xc8, 0x9c, 0x63, 0xd2, 0xa8, 0xbc, 0xcf, 0x23, 0xe4, 0x05, 0x45, + 0xb8, 0x56, 0xf9, 0x8f, 0xba, 0xb6, 0xbf, 0x13, 0x4c, 0x8a, 0x3e, 0xf4, 0x2f, 0xdb, 0xdf, 0x90, + 0x12, 0x0c, 0xf3, 0x41, 0x25, 0xbf, 0xed, 0x3f, 0xf9, 0xfa, 0xed, 0x41, 0x3b, 0xe1, 0x98, 0x8e, + 0x98, 0x1b, 0xc9, 0x13, 0x4f, 0x8f, 0x8a, 0x52, 0xca, 0xc5, 0xf9, 0x83, 0x87, 0x93, 0x1c, 0x94, + 0xeb, 0x1f, 0x76, 0xf7, 0x1e, 0xef, 0x74, 0x47, 0xec, 0x35, 0x4c, 0x82, 0x65, 0x86, 0x51, 0x77, + 0x60, 0x1e, 0x93, 0xf5, 0x38, 0x0f, 0xcf, 0x8c, 0x61, 0xc6, 0x15, 0x5a, 0xf5, 0xe6, 0xd2, 0x7f, + 0x68, 0xd7, 0xe2, 0xdc, 0x2f, 0xc5, 0x1d, 0xae, 0xd0, 0xdc, 0x22, 0x1b, 0x7a, 0xbb, 0x21, 0x8e, + 0xc3, 0x94, 0xaa, 0xd4, 0x5a, 0x6a, 0x1a, 0xad, 0xd5, 0xe0, 0x8e, 0x7e, 0x7d, 0x34, 0x7e, 0x45, + 0x55, 0x6a, 0x6e, 0x93, 0xbb, 0xa3, 0x6a, 0xb3, 0xf3, 0xe4, 0x8d, 0x8a, 0xdc, 0xb8, 0xf8, 0xa0, + 0xd9, 0x43, 0x42, 0xe2, 0xa1, 0x3c, 0x09, 0x15, 0x52, 0x04, 0x6b, 0xb9, 0x69, 0xb4, 0xd6, 0xdb, + 0xdb, 0xff, 0x52, 0x70, 0x0f, 0x29, 0x8e, 0x54, 0xb0, 0x5a, 0xa6, 0xcb, 0x35, 0x38, 0x31, 0xb9, + 0x5f, 0x15, 0xdd, 0x83, 0x0c, 0xca, 0x93, 0x84, 0x5e, 0x46, 0x55, 0xca, 0x45, 0x62, 0x76, 0xc8, + 0x2d, 0x28, 0x7f, 0x23, 0x11, 0x81, 0x3e, 0xc3, 0x9d, 0x05, 0x23, 0xfe, 0xc8, 0x1e, 0xe8, 0x5c, + 0x70, 0x61, 0xf0, 0x3b, 0x9f, 0xa7, 0xb6, 0x71, 0x3a, 0xb5, 0x8d, 0xef, 0x53, 0xdb, 0xf8, 0x34, + 0xb3, 0x6b, 0xa7, 0x33, 0xbb, 0xf6, 0x65, 0x66, 0xd7, 0x3e, 0xfc, 0xb5, 0xe1, 0xf1, 0xfc, 0xbd, + 0xa9, 0xea, 0x66, 0x2b, 0xd5, 0x85, 0xd9, 0xfb, 0x19, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x88, 0x9f, + 0xc0, 0xab, 0x03, 0x00, 0x00, +} + +func (m *EventNewFinalityProvider) 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 *EventNewFinalityProvider) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventNewFinalityProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Fp != nil { + { + size, err := m.Fp.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventNewBTCDelegation) 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 *EventNewBTCDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventNewBTCDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BtcDel != nil { + { + size, err := m.BtcDel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventActivateBTCDelegation) 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 *EventActivateBTCDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventActivateBTCDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BtcDel != nil { + { + size, err := m.BtcDel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventUnbondedBTCDelegation) 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 *EventUnbondedBTCDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventUnbondedBTCDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FromState != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.FromState)) + i-- + dAtA[i] = 0x28 + } + if len(m.UnbondingTxHash) > 0 { + i -= len(m.UnbondingTxHash) + copy(dAtA[i:], m.UnbondingTxHash) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UnbondingTxHash))) + i-- + dAtA[i] = 0x22 + } + if len(m.StakingTxHash) > 0 { + i -= len(m.StakingTxHash) + copy(dAtA[i:], m.StakingTxHash) + i = encodeVarintEvents(dAtA, i, uint64(len(m.StakingTxHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.FpBtcPkList) > 0 { + for iNdEx := len(m.FpBtcPkList) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.FpBtcPkList[iNdEx].Size() + i -= size + if _, err := m.FpBtcPkList[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventSelectiveSlashing) 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 *EventSelectiveSlashing) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventSelectiveSlashing) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Evidence != nil { + { + size, err := m.Evidence.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventNewFinalityProvider) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Fp != nil { + l = m.Fp.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventNewBTCDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcDel != nil { + l = m.BtcDel.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventActivateBTCDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcDel != nil { + l = m.BtcDel.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventUnbondedBTCDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if len(m.FpBtcPkList) > 0 { + for _, e := range m.FpBtcPkList { + l = e.Size() + n += 1 + l + sovEvents(uint64(l)) + } + } + l = len(m.StakingTxHash) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.UnbondingTxHash) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.FromState != 0 { + n += 1 + sovEvents(uint64(m.FromState)) + } + return n +} + +func (m *EventSelectiveSlashing) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Evidence != nil { + l = m.Evidence.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventNewFinalityProvider) 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 ErrIntOverflowEvents + } + 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: EventNewFinalityProvider: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventNewFinalityProvider: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Fp == nil { + m.Fp = &FinalityProvider{} + } + if err := m.Fp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventNewBTCDelegation) 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 ErrIntOverflowEvents + } + 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: EventNewBTCDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventNewBTCDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcDel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BtcDel == nil { + m.BtcDel = &BTCDelegation{} + } + if err := m.BtcDel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventActivateBTCDelegation) 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 ErrIntOverflowEvents + } + 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: EventActivateBTCDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventActivateBTCDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcDel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BtcDel == nil { + m.BtcDel = &BTCDelegation{} + } + if err := m.BtcDel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventUnbondedBTCDelegation) 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 ErrIntOverflowEvents + } + 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: EventUnbondedBTCDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventUnbondedBTCDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPkList = append(m.FpBtcPkList, v) + if err := m.FpBtcPkList[len(m.FpBtcPkList)-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 StakingTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnbondingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FromState", wireType) + } + m.FromState = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FromState |= BTCDelegationStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventSelectiveSlashing) 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 ErrIntOverflowEvents + } + 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: EventSelectiveSlashing: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventSelectiveSlashing: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Evidence == nil { + m.Evidence = &SelectiveSlashingEvidence{} + } + if err := m.Evidence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/expected_keepers.go b/x/btcstaking/types/expected_keepers.go new file mode 100644 index 000000000..7d5a8a1ed --- /dev/null +++ b/x/btcstaking/types/expected_keepers.go @@ -0,0 +1,20 @@ +package types + +import ( + "context" + "math/big" + + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" +) + +type BTCLightClientKeeper interface { + GetTipInfo(ctx context.Context) *btclctypes.BTCHeaderInfo + GetHeaderByHash(ctx context.Context, hash *bbn.BTCHeaderHashBytes) *btclctypes.BTCHeaderInfo +} + +type BtcCheckpointKeeper interface { + GetPowLimit() *big.Int + GetParams(ctx context.Context) (p btcctypes.Params) +} diff --git a/x/btcstaking/types/genesis.go b/x/btcstaking/types/genesis.go new file mode 100644 index 000000000..ab1a56d77 --- /dev/null +++ b/x/btcstaking/types/genesis.go @@ -0,0 +1,16 @@ +package types + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + + return gs.Params.Validate() +} diff --git a/x/btcstaking/types/genesis.pb.go b/x/btcstaking/types/genesis.pb.go new file mode 100644 index 000000000..2ccb1d1d9 --- /dev/null +++ b/x/btcstaking/types/genesis.pb.go @@ -0,0 +1,323 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/genesis.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 + +// GenesisState defines the btcstaking module's genesis state. +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_85d7b95fa5620238, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "babylon.btcstaking.v1.GenesisState") +} + +func init() { + proto.RegisterFile("babylon/btcstaking/v1/genesis.proto", fileDescriptor_85d7b95fa5620238) +} + +var fileDescriptor_85d7b95fa5620238 = []byte{ + // 202 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4e, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0x2e, 0x2e, 0x49, 0xcc, 0xce, 0xcc, 0x4b, 0xd7, 0x2f, + 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x12, 0x85, 0x2a, 0xd2, 0x43, 0x28, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, + 0xab, 0xd0, 0x07, 0xb1, 0x20, 0x8a, 0xa5, 0x94, 0xb0, 0x9b, 0x58, 0x90, 0x58, 0x94, 0x98, 0x0b, + 0x35, 0x50, 0xc9, 0x9b, 0x8b, 0xc7, 0x1d, 0x62, 0x43, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x35, + 0x17, 0x1b, 0x44, 0x5e, 0x82, 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x56, 0x0f, 0xab, 0x8d, 0x7a, + 0x01, 0x60, 0x45, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0x41, 0xb5, 0x38, 0xf9, 0x9c, 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, 0x51, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, + 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0xd4, 0xc0, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x18, 0x47, 0xbf, 0x02, + 0xd9, 0x91, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x17, 0x1a, 0x03, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x64, 0xf3, 0x4d, 0xdf, 0x19, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) 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 = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: 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 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:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/genesis_test.go b/x/btcstaking/types/genesis_test.go new file mode 100644 index 000000000..00ee5d73d --- /dev/null +++ b/x/btcstaking/types/genesis_test.go @@ -0,0 +1,64 @@ +package types_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/stretchr/testify/require" +) + +func TestGenesisState_Validate(t *testing.T) { + tests := []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + { + desc: "valid genesis state", + genState: &types.GenesisState{ + Params: types.Params{ + CovenantPks: types.DefaultParams().CovenantPks, + CovenantQuorum: types.DefaultParams().CovenantQuorum, + SlashingAddress: types.DefaultParams().SlashingAddress, + MinSlashingTxFeeSat: 500, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), + SlashingRate: sdkmath.LegacyMustNewDecFromStr("0.1"), + MaxActiveFinalityProviders: 100, + }, + }, + valid: true, + }, + { + desc: "invalid slashing rate in genesis", + genState: &types.GenesisState{ + Params: types.Params{ + CovenantPks: types.DefaultParams().CovenantPks, + CovenantQuorum: types.DefaultParams().CovenantQuorum, + SlashingAddress: types.DefaultParams().SlashingAddress, + MinSlashingTxFeeSat: 500, + MinCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.5"), + SlashingRate: sdkmath.LegacyZeroDec(), // invalid slashing rate + MaxActiveFinalityProviders: 100, + }, + }, + valid: false, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/btcstaking/types/incentive.go b/x/btcstaking/types/incentive.go new file mode 100644 index 000000000..6b2d9dd9f --- /dev/null +++ b/x/btcstaking/types/incentive.go @@ -0,0 +1,78 @@ +package types + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewRewardDistCache() *RewardDistCache { + return &RewardDistCache{ + TotalVotingPower: 0, + FinalityProviders: []*FinalityProviderDistInfo{}, + } +} + +func (rdc *RewardDistCache) AddFinalityProviderDistInfo(v *FinalityProviderDistInfo) { + if v.TotalVotingPower > 0 { + // append finality provider dist info and accumulate voting power + rdc.FinalityProviders = append(rdc.FinalityProviders, v) + rdc.TotalVotingPower += v.TotalVotingPower + } +} + +// FilterVotedFinalityProviders filters out finality providers that have voted according to a map of given voters +// and update total voted power accordingly +func (rdc *RewardDistCache) FilterVotedFinalityProviders(voterBTCPKs map[string]struct{}) { + filteredFps := []*FinalityProviderDistInfo{} + totalVotingPower := uint64(0) + for _, v := range rdc.FinalityProviders { + if _, ok := voterBTCPKs[v.BtcPk.MarshalHex()]; ok { + filteredFps = append(filteredFps, v) + totalVotingPower += v.TotalVotingPower + } + } + rdc.FinalityProviders = filteredFps + rdc.TotalVotingPower = totalVotingPower +} + +// GetFinalityProviderPortion returns the portion of a finality provider's voting power out of the total voting power +func (rdc *RewardDistCache) GetFinalityProviderPortion(v *FinalityProviderDistInfo) sdkmath.LegacyDec { + return sdkmath.LegacyNewDec(int64(v.TotalVotingPower)).QuoTruncate(sdkmath.LegacyNewDec(int64(rdc.TotalVotingPower))) +} + +func NewFinalityProviderDistInfo(fp *FinalityProvider) *FinalityProviderDistInfo { + return &FinalityProviderDistInfo{ + BtcPk: fp.BtcPk, + BabylonPk: fp.BabylonPk, + Commission: fp.Commission, + TotalVotingPower: 0, + BtcDels: []*BTCDelDistInfo{}, + } +} + +func (v *FinalityProviderDistInfo) GetAddress() sdk.AccAddress { + return sdk.AccAddress(v.BabylonPk.Address()) +} + +func (v *FinalityProviderDistInfo) AddBTCDel(btcDel *BTCDelegation, btcHeight uint64, wValue uint64, covenantQuorum uint32) { + btcDelDistInfo := &BTCDelDistInfo{ + BabylonPk: btcDel.BabylonPk, + VotingPower: btcDel.VotingPower(btcHeight, wValue, covenantQuorum), + } + + if btcDelDistInfo.VotingPower > 0 { + // if this BTC delegation has voting power, append it and accumulate voting power + v.BtcDels = append(v.BtcDels, btcDelDistInfo) + v.TotalVotingPower += btcDelDistInfo.VotingPower + } +} + +// GetBTCDelPortion returns the portion of a BTC delegation's voting power out of +// the finality provider's total voting power +func (v *FinalityProviderDistInfo) GetBTCDelPortion(d *BTCDelDistInfo) sdkmath.LegacyDec { + return sdkmath.LegacyNewDec(int64(d.VotingPower)).QuoTruncate(sdkmath.LegacyNewDec(int64(v.TotalVotingPower))) +} + +func (d *BTCDelDistInfo) GetAddress() sdk.AccAddress { + return sdk.AccAddress(d.BabylonPk.Address()) +} diff --git a/x/btcstaking/types/incentive.pb.go b/x/btcstaking/types/incentive.pb.go new file mode 100644 index 000000000..2ecf02d12 --- /dev/null +++ b/x/btcstaking/types/incentive.pb.go @@ -0,0 +1,995 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/incentive.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "github.com/cosmos/cosmos-proto" + secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + _ "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 + +// RewardDistCache is the cache for reward distribution of finality providers at a height +type RewardDistCache struct { + TotalVotingPower uint64 `protobuf:"varint,1,opt,name=total_voting_power,json=totalVotingPower,proto3" json:"total_voting_power,omitempty"` + // finality_providers is a list of finality providers' voting power information + FinalityProviders []*FinalityProviderDistInfo `protobuf:"bytes,2,rep,name=finality_providers,json=finalityProviders,proto3" json:"finality_providers,omitempty"` +} + +func (m *RewardDistCache) Reset() { *m = RewardDistCache{} } +func (m *RewardDistCache) String() string { return proto.CompactTextString(m) } +func (*RewardDistCache) ProtoMessage() {} +func (*RewardDistCache) Descriptor() ([]byte, []int) { + return fileDescriptor_ac354c3bd6d7a66b, []int{0} +} +func (m *RewardDistCache) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RewardDistCache) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RewardDistCache.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 *RewardDistCache) XXX_Merge(src proto.Message) { + xxx_messageInfo_RewardDistCache.Merge(m, src) +} +func (m *RewardDistCache) XXX_Size() int { + return m.Size() +} +func (m *RewardDistCache) XXX_DiscardUnknown() { + xxx_messageInfo_RewardDistCache.DiscardUnknown(m) +} + +var xxx_messageInfo_RewardDistCache proto.InternalMessageInfo + +func (m *RewardDistCache) GetTotalVotingPower() uint64 { + if m != nil { + return m.TotalVotingPower + } + return 0 +} + +func (m *RewardDistCache) GetFinalityProviders() []*FinalityProviderDistInfo { + if m != nil { + return m.FinalityProviders + } + return nil +} + +// FinalityProviderDistInfo is the reward distribution of a finality provider and its BTC delegations +type FinalityProviderDistInfo struct { + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // babylon_pk is the Babylon public key of the finality provider + BabylonPk *secp256k1.PubKey `protobuf:"bytes,2,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // commission defines the commission rate of finality provider + Commission *cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=commission,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"commission,omitempty"` + // total_voting_power is the total voting power of the finality provider + TotalVotingPower uint64 `protobuf:"varint,4,opt,name=total_voting_power,json=totalVotingPower,proto3" json:"total_voting_power,omitempty"` + // btc_dels is a list of BTC delegations' voting power information under this finality provider + BtcDels []*BTCDelDistInfo `protobuf:"bytes,5,rep,name=btc_dels,json=btcDels,proto3" json:"btc_dels,omitempty"` +} + +func (m *FinalityProviderDistInfo) Reset() { *m = FinalityProviderDistInfo{} } +func (m *FinalityProviderDistInfo) String() string { return proto.CompactTextString(m) } +func (*FinalityProviderDistInfo) ProtoMessage() {} +func (*FinalityProviderDistInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_ac354c3bd6d7a66b, []int{1} +} +func (m *FinalityProviderDistInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FinalityProviderDistInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FinalityProviderDistInfo.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 *FinalityProviderDistInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_FinalityProviderDistInfo.Merge(m, src) +} +func (m *FinalityProviderDistInfo) XXX_Size() int { + return m.Size() +} +func (m *FinalityProviderDistInfo) XXX_DiscardUnknown() { + xxx_messageInfo_FinalityProviderDistInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_FinalityProviderDistInfo proto.InternalMessageInfo + +func (m *FinalityProviderDistInfo) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *FinalityProviderDistInfo) GetTotalVotingPower() uint64 { + if m != nil { + return m.TotalVotingPower + } + return 0 +} + +func (m *FinalityProviderDistInfo) GetBtcDels() []*BTCDelDistInfo { + if m != nil { + return m.BtcDels + } + return nil +} + +// BTCDelDistInfo contains the information related to reward distribution for a BTC delegations +type BTCDelDistInfo struct { + // babylon_pk is the Babylon public key of the BTC delegations + BabylonPk *secp256k1.PubKey `protobuf:"bytes,1,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // voting_power is the voting power of the BTC delegation + VotingPower uint64 `protobuf:"varint,2,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` +} + +func (m *BTCDelDistInfo) Reset() { *m = BTCDelDistInfo{} } +func (m *BTCDelDistInfo) String() string { return proto.CompactTextString(m) } +func (*BTCDelDistInfo) ProtoMessage() {} +func (*BTCDelDistInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_ac354c3bd6d7a66b, []int{2} +} +func (m *BTCDelDistInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCDelDistInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCDelDistInfo.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 *BTCDelDistInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCDelDistInfo.Merge(m, src) +} +func (m *BTCDelDistInfo) XXX_Size() int { + return m.Size() +} +func (m *BTCDelDistInfo) XXX_DiscardUnknown() { + xxx_messageInfo_BTCDelDistInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCDelDistInfo proto.InternalMessageInfo + +func (m *BTCDelDistInfo) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *BTCDelDistInfo) GetVotingPower() uint64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +func init() { + proto.RegisterType((*RewardDistCache)(nil), "babylon.btcstaking.v1.RewardDistCache") + proto.RegisterType((*FinalityProviderDistInfo)(nil), "babylon.btcstaking.v1.FinalityProviderDistInfo") + proto.RegisterType((*BTCDelDistInfo)(nil), "babylon.btcstaking.v1.BTCDelDistInfo") +} + +func init() { + proto.RegisterFile("babylon/btcstaking/v1/incentive.proto", fileDescriptor_ac354c3bd6d7a66b) +} + +var fileDescriptor_ac354c3bd6d7a66b = []byte{ + // 491 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0x86, 0xe3, 0xf4, 0x02, 0x9d, 0x54, 0x5c, 0x2c, 0x90, 0x4c, 0x91, 0x9c, 0x10, 0xa9, 0x52, + 0x16, 0x74, 0x86, 0xa4, 0xd0, 0x25, 0x42, 0xae, 0x85, 0x54, 0xd1, 0x4a, 0x96, 0x85, 0x58, 0xb0, + 0x20, 0x9a, 0x99, 0x4c, 0x9c, 0x91, 0xed, 0x19, 0xcb, 0x33, 0x75, 0xf1, 0x5b, 0xf0, 0x06, 0xbc, + 0x04, 0x0f, 0xc1, 0xb2, 0x62, 0x85, 0xba, 0xa8, 0x50, 0xb2, 0xe1, 0x31, 0x90, 0xed, 0xa1, 0x37, + 0x11, 0xa1, 0xee, 0x72, 0xf2, 0xff, 0xe7, 0x9c, 0xdf, 0xdf, 0xb1, 0xc1, 0x36, 0xc1, 0xa4, 0x4c, + 0xa4, 0x40, 0x44, 0x53, 0xa5, 0x71, 0xcc, 0x45, 0x84, 0x8a, 0x21, 0xe2, 0x82, 0x32, 0xa1, 0x79, + 0xc1, 0x60, 0x96, 0x4b, 0x2d, 0xed, 0xc7, 0xc6, 0x06, 0x2f, 0x6d, 0xb0, 0x18, 0x6e, 0x3d, 0x8a, + 0x64, 0x24, 0x6b, 0x07, 0xaa, 0x7e, 0x35, 0xe6, 0xad, 0x27, 0x54, 0xaa, 0x54, 0xaa, 0x71, 0x23, + 0x34, 0x85, 0x91, 0xfa, 0x4d, 0x85, 0x68, 0x5e, 0x66, 0x5a, 0x22, 0xc5, 0x68, 0x36, 0x7a, 0xb5, + 0x17, 0x0f, 0x51, 0xcc, 0x4a, 0xe3, 0xe9, 0x7f, 0xb5, 0xc0, 0xfd, 0x90, 0x9d, 0xe0, 0x7c, 0xe2, + 0x73, 0xa5, 0xf7, 0x31, 0x9d, 0x31, 0xfb, 0x39, 0xb0, 0xb5, 0xd4, 0x38, 0x19, 0x17, 0x52, 0x73, + 0x11, 0x8d, 0x33, 0x79, 0xc2, 0x72, 0xc7, 0xea, 0x59, 0x83, 0xd5, 0xf0, 0x41, 0xad, 0x7c, 0xa8, + 0x85, 0xa0, 0xfa, 0xdf, 0xfe, 0x04, 0xec, 0x29, 0x17, 0x38, 0xe1, 0xba, 0xac, 0x42, 0x14, 0x7c, + 0xc2, 0x72, 0xe5, 0xb4, 0x7b, 0x2b, 0x83, 0xce, 0x08, 0xc1, 0x7f, 0x3e, 0x0a, 0x7c, 0x6b, 0x1a, + 0x02, 0xe3, 0xaf, 0x76, 0x1f, 0x88, 0xa9, 0x0c, 0x1f, 0x4e, 0x6f, 0x28, 0xaa, 0xff, 0xbb, 0x0d, + 0x9c, 0x65, 0x7e, 0xfb, 0x08, 0xac, 0x13, 0x4d, 0xc7, 0x59, 0x5c, 0xc7, 0xdb, 0xf4, 0xf6, 0xce, + 0xce, 0xbb, 0xa3, 0x88, 0xeb, 0xd9, 0x31, 0x81, 0x54, 0xa6, 0xc8, 0xac, 0xa7, 0x33, 0xcc, 0xc5, + 0xdf, 0x02, 0xe9, 0x32, 0x63, 0x0a, 0x7a, 0x07, 0xc1, 0xee, 0xcb, 0x17, 0xc1, 0x31, 0x79, 0xc7, + 0xca, 0x70, 0x8d, 0x68, 0x1a, 0xc4, 0xf6, 0x6b, 0x00, 0x8c, 0xa9, 0x1a, 0xd9, 0xee, 0x59, 0x83, + 0xce, 0xa8, 0x0b, 0x0d, 0xd4, 0x06, 0x23, 0xbc, 0xc0, 0x08, 0x4d, 0xef, 0x86, 0x69, 0x09, 0x62, + 0xfb, 0x08, 0x00, 0x2a, 0xd3, 0x94, 0x2b, 0xc5, 0xa5, 0x70, 0x56, 0x7a, 0xd6, 0x60, 0xc3, 0xdb, + 0x39, 0x3b, 0xef, 0x3e, 0x6d, 0x46, 0xa8, 0x49, 0x0c, 0xb9, 0x44, 0x29, 0xd6, 0x33, 0x78, 0xc8, + 0x22, 0x4c, 0x4b, 0x9f, 0xd1, 0x1f, 0xdf, 0x76, 0x80, 0xd9, 0xe0, 0x33, 0x1a, 0x5e, 0x19, 0xb0, + 0xe4, 0x10, 0xab, 0x4b, 0x0e, 0xf1, 0x06, 0xdc, 0xad, 0x58, 0x4c, 0x58, 0xa2, 0x9c, 0xb5, 0x1a, + 0xff, 0xf6, 0x12, 0xfc, 0xde, 0xfb, 0x7d, 0x9f, 0x25, 0x17, 0xd0, 0xef, 0x10, 0x4d, 0x7d, 0x96, + 0xa8, 0xbe, 0x02, 0xf7, 0xae, 0x4b, 0x37, 0x80, 0x58, 0xb7, 0x06, 0xf2, 0x0c, 0x6c, 0x5e, 0xcb, + 0xde, 0xae, 0xb3, 0x77, 0x8a, 0xcb, 0xd8, 0xde, 0xe1, 0xf7, 0xb9, 0x6b, 0x9d, 0xce, 0x5d, 0xeb, + 0xd7, 0xdc, 0xb5, 0xbe, 0x2c, 0xdc, 0xd6, 0xe9, 0xc2, 0x6d, 0xfd, 0x5c, 0xb8, 0xad, 0x8f, 0xff, + 0x3d, 0xe4, 0xe7, 0xab, 0x1f, 0x52, 0x7d, 0x55, 0xb2, 0x5e, 0xbf, 0xd6, 0xbb, 0x7f, 0x02, 0x00, + 0x00, 0xff, 0xff, 0xef, 0xcd, 0x33, 0x90, 0x6b, 0x03, 0x00, 0x00, +} + +func (m *RewardDistCache) 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 *RewardDistCache) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RewardDistCache) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FinalityProviders) > 0 { + for iNdEx := len(m.FinalityProviders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FinalityProviders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.TotalVotingPower != 0 { + i = encodeVarintIncentive(dAtA, i, uint64(m.TotalVotingPower)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *FinalityProviderDistInfo) 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 *FinalityProviderDistInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FinalityProviderDistInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BtcDels) > 0 { + for iNdEx := len(m.BtcDels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BtcDels[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.TotalVotingPower != 0 { + i = encodeVarintIncentive(dAtA, i, uint64(m.TotalVotingPower)) + i-- + dAtA[i] = 0x20 + } + if m.Commission != nil { + { + size := m.Commission.Size() + i -= size + if _, err := m.Commission.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BTCDelDistInfo) 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 *BTCDelDistInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCDelDistInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.VotingPower != 0 { + i = encodeVarintIncentive(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x10 + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintIncentive(dAtA []byte, offset int, v uint64) int { + offset -= sovIncentive(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *RewardDistCache) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TotalVotingPower != 0 { + n += 1 + sovIncentive(uint64(m.TotalVotingPower)) + } + if len(m.FinalityProviders) > 0 { + for _, e := range m.FinalityProviders { + l = e.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + } + return n +} + +func (m *FinalityProviderDistInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + if m.Commission != nil { + l = m.Commission.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + if m.TotalVotingPower != 0 { + n += 1 + sovIncentive(uint64(m.TotalVotingPower)) + } + if len(m.BtcDels) > 0 { + for _, e := range m.BtcDels { + l = e.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + } + return n +} + +func (m *BTCDelDistInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + if m.VotingPower != 0 { + n += 1 + sovIncentive(uint64(m.VotingPower)) + } + return n +} + +func sovIncentive(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozIncentive(x uint64) (n int) { + return sovIncentive(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *RewardDistCache) 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 ErrIntOverflowIncentive + } + 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: RewardDistCache: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RewardDistCache: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalVotingPower", wireType) + } + m.TotalVotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalVotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalityProviders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FinalityProviders = append(m.FinalityProviders, &FinalityProviderDistInfo{}) + if err := m.FinalityProviders[len(m.FinalityProviders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIncentive(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIncentive + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FinalityProviderDistInfo) 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 ErrIntOverflowIncentive + } + 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: FinalityProviderDistInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FinalityProviderDistInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + 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 ErrInvalidLengthIncentive + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.LegacyDec + m.Commission = &v + if err := m.Commission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalVotingPower", wireType) + } + m.TotalVotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalVotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcDels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BtcDels = append(m.BtcDels, &BTCDelDistInfo{}) + if err := m.BtcDels[len(m.BtcDels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIncentive(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIncentive + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCDelDistInfo) 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 ErrIntOverflowIncentive + } + 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: BTCDelDistInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCDelDistInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipIncentive(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIncentive + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipIncentive(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, ErrIntOverflowIncentive + } + 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, ErrIntOverflowIncentive + } + 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, ErrIntOverflowIncentive + } + 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, ErrInvalidLengthIncentive + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupIncentive + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthIncentive + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthIncentive = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIncentive = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupIncentive = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/keys.go b/x/btcstaking/types/keys.go new file mode 100644 index 000000000..4f291ad14 --- /dev/null +++ b/x/btcstaking/types/keys.go @@ -0,0 +1,29 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "btcstaking" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_btcstaking" +) + +var ( + ParamsKey = []byte{0x01} // key prefix for the parameters + FinalityProviderKey = []byte{0x02} // key prefix for the finality providers + BTCDelegatorKey = []byte{0x03} // key prefix for the BTC delegators + BTCDelegationKey = []byte{0x04} // key prefix for the BTC delegations + VotingPowerKey = []byte{0x05} // key prefix for the voting power + BTCHeightKey = []byte{0x06} // key prefix for the BTC heights + RewardDistCacheKey = []byte{0x07} // key prefix for reward distribution cache +) + +func KeyPrefix(p string) []byte { + return []byte(p) +} diff --git a/x/btcstaking/types/mocked_keepers.go b/x/btcstaking/types/mocked_keepers.go new file mode 100644 index 000000000..3ab694c07 --- /dev/null +++ b/x/btcstaking/types/mocked_keepers.go @@ -0,0 +1,118 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/btcstaking/types/expected_keepers.go + +// Package types is a generated GoMock package. +package types + +import ( + context "context" + big "math/big" + reflect "reflect" + + types "github.com/babylonchain/babylon/types" + types0 "github.com/babylonchain/babylon/x/btccheckpoint/types" + types1 "github.com/babylonchain/babylon/x/btclightclient/types" + gomock "github.com/golang/mock/gomock" +) + +// 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 +} + +// GetHeaderByHash mocks base method. +func (m *MockBTCLightClientKeeper) GetHeaderByHash(ctx context.Context, hash *types.BTCHeaderHashBytes) *types1.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHeaderByHash", ctx, hash) + ret0, _ := ret[0].(*types1.BTCHeaderInfo) + return ret0 +} + +// GetHeaderByHash indicates an expected call of GetHeaderByHash. +func (mr *MockBTCLightClientKeeperMockRecorder) GetHeaderByHash(ctx, hash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHeaderByHash", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetHeaderByHash), ctx, hash) +} + +// GetTipInfo mocks base method. +func (m *MockBTCLightClientKeeper) GetTipInfo(ctx context.Context) *types1.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTipInfo", ctx) + ret0, _ := ret[0].(*types1.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 + recorder *MockBtcCheckpointKeeperMockRecorder +} + +// MockBtcCheckpointKeeperMockRecorder is the mock recorder for MockBtcCheckpointKeeper. +type MockBtcCheckpointKeeperMockRecorder struct { + mock *MockBtcCheckpointKeeper +} + +// NewMockBtcCheckpointKeeper creates a new mock instance. +func NewMockBtcCheckpointKeeper(ctrl *gomock.Controller) *MockBtcCheckpointKeeper { + mock := &MockBtcCheckpointKeeper{ctrl: ctrl} + mock.recorder = &MockBtcCheckpointKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBtcCheckpointKeeper) EXPECT() *MockBtcCheckpointKeeperMockRecorder { + return m.recorder +} + +// GetParams mocks base method. +func (m *MockBtcCheckpointKeeper) GetParams(ctx context.Context) types0.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetParams", ctx) + ret0, _ := ret[0].(types0.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) +} + +// GetPowLimit mocks base method. +func (m *MockBtcCheckpointKeeper) GetPowLimit() *big.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPowLimit") + ret0, _ := ret[0].(*big.Int) + return ret0 +} + +// GetPowLimit indicates an expected call of GetPowLimit. +func (mr *MockBtcCheckpointKeeperMockRecorder) GetPowLimit() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPowLimit", reflect.TypeOf((*MockBtcCheckpointKeeper)(nil).GetPowLimit)) +} diff --git a/x/btcstaking/types/msg.go b/x/btcstaking/types/msg.go new file mode 100644 index 000000000..73247bc0c --- /dev/null +++ b/x/btcstaking/types/msg.go @@ -0,0 +1,165 @@ +package types + +import ( + "fmt" + math "math" + + "github.com/babylonchain/babylon/btcstaking" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/chaincfg/chainhash" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure that these message types implement the sdk.Msg interface +var ( + _ sdk.Msg = &MsgUpdateParams{} + _ sdk.Msg = &MsgCreateFinalityProvider{} + _ sdk.Msg = &MsgCreateBTCDelegation{} + _ sdk.Msg = &MsgAddCovenantSigs{} + _ sdk.Msg = &MsgBTCUndelegate{} +) + +func (m *MsgCreateFinalityProvider) ValidateBasic() error { + if m.Commission == nil { + return fmt.Errorf("empty commission") + } + if m.Description == nil { + return fmt.Errorf("empty description") + } + if len(m.Description.Moniker) == 0 { + return fmt.Errorf("empty moniker") + } + if _, err := m.Description.EnsureLength(); err != nil { + return err + } + if m.BabylonPk == nil { + return fmt.Errorf("empty Babylon public key") + } + if m.BtcPk == nil { + return fmt.Errorf("empty BTC public key") + } + if _, err := m.BtcPk.ToBTCPK(); err != nil { + return fmt.Errorf("invalid BTC public key: %v", err) + } + if m.Pop == nil { + return fmt.Errorf("empty proof of possession") + } + if _, err := sdk.AccAddressFromBech32(m.Signer); err != nil { + return err + } + if err := m.Pop.ValidateBasic(); err != nil { + return err + } + + return nil +} + +func (m *MsgCreateBTCDelegation) ValidateBasic() error { + if m.BabylonPk == nil { + return fmt.Errorf("empty Babylon public key") + } + if m.Pop == nil { + return fmt.Errorf("empty proof of possession") + } + if m.BtcPk == nil { + return fmt.Errorf("empty delegator BTC public key") + } + if _, err := m.BtcPk.ToBTCPK(); err != nil { + return fmt.Errorf("invalid BTC public key: %v", err) + } + if m.StakingTx == nil { + return fmt.Errorf("empty staking tx info") + } + if m.SlashingTx == nil { + return fmt.Errorf("empty slashing tx") + } + if m.DelegatorSlashingSig == nil { + return fmt.Errorf("empty delegator signature") + } + if _, err := sdk.AccAddressFromBech32(m.Signer); err != nil { + return err + } + + // Check staking time is at most uint16 + if m.StakingTime > math.MaxUint16 { + return ErrInvalidStakingTx.Wrapf("invalid lock time: %d, max: %d", m.StakingTime, math.MaxUint16) + } + // Ensure list of finality provider BTC PKs is not empty + if len(m.FpBtcPkList) == 0 { + return ErrEmptyFpList + } + // Ensure list of finality provider BTC PKs is not duplicated + if ExistsDup(m.FpBtcPkList) { + return ErrDuplicatedFp + } + + // staking tx should be correctly formatted + if err := m.StakingTx.ValidateBasic(); err != nil { + return err + } + if err := m.Pop.ValidateBasic(); err != nil { + return err + } + + // verifications about on-demand unbonding + if m.UnbondingTx == nil { + return fmt.Errorf("empty unbonding tx") + } + if m.UnbondingSlashingTx == nil { + return fmt.Errorf("empty slashing tx") + } + if m.DelegatorUnbondingSlashingSig == nil { + return fmt.Errorf("empty delegator signature") + } + unbondingTxMsg, err := bbn.NewBTCTxFromBytes(m.UnbondingTx) + if err != nil { + return err + } + if err := btcstaking.IsSimpleTransfer(unbondingTxMsg); err != nil { + return err + } + + // Check unbonding time is lower than max uint16 + if m.UnbondingTime > math.MaxUint16 { + return ErrInvalidUnbondingTx.Wrapf("unbonding time %d must be lower than %d", m.UnbondingTime, math.MaxUint16) + } + + return nil +} + +func (m *MsgAddCovenantSigs) ValidateBasic() error { + if m.Pk == nil { + return fmt.Errorf("empty BTC covenant public key") + } + if _, err := m.Pk.ToBTCPK(); err != nil { + return fmt.Errorf("invalid BTC public key: %v", err) + } + if m.SlashingTxSigs == nil { + return fmt.Errorf("empty covenant signatures on slashing tx") + } + if len(m.StakingTxHash) != chainhash.MaxHashStringSize { + return fmt.Errorf("staking tx hash is not %d", chainhash.MaxHashStringSize) + } + + // verifications about on-demand unbonding + if m.UnbondingTxSig == nil { + return fmt.Errorf("empty covenant signature") + } + if m.SlashingUnbondingTxSigs == nil { + return fmt.Errorf("empty covenant signature") + } + + return nil +} + +func (m *MsgBTCUndelegate) ValidateBasic() error { + if len(m.StakingTxHash) != chainhash.MaxHashStringSize { + return fmt.Errorf("staking tx hash is not %d", chainhash.MaxHashStringSize) + } + + if m.UnbondingTxSig == nil { + return fmt.Errorf("empty signature from the delegator") + } + + return nil +} diff --git a/x/btcstaking/types/params.go b/x/btcstaking/types/params.go new file mode 100644 index 000000000..8e923fca7 --- /dev/null +++ b/x/btcstaking/types/params.go @@ -0,0 +1,185 @@ +package types + +import ( + "fmt" + "math" + + sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/btcstaking" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/cometbft/cometbft/crypto/tmhash" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "gopkg.in/yaml.v2" +) + +const ( + defaultMaxActiveFinalityProviders uint32 = 100 +) + +var _ paramtypes.ParamSet = (*Params)(nil) + +// DefaultCovenantCommittee deterministically generates a covenant committee +// with 5 members and quorum size of 3 +func DefaultCovenantCommittee() ([]*btcec.PrivateKey, []*btcec.PublicKey, uint32) { + sks, pks := []*btcec.PrivateKey{}, []*btcec.PublicKey{} + for i := uint8(0); i < 5; i++ { + skBytes := tmhash.Sum([]byte{i}) + sk, pk := btcec.PrivKeyFromBytes(skBytes) + sks = append(sks, sk) + pks = append(pks, pk) + } + return sks, pks, 3 +} + +func defaultSlashingAddress() string { + // 20 bytes + pkHash := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + addr, err := btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.SimNetParams) + if err != nil { + panic(err) + } + return addr.EncodeAddress() +} + +// ParamKeyTable the param key table for launch module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + _, pks, quorum := DefaultCovenantCommittee() + return Params{ + CovenantPks: bbn.NewBIP340PKsFromBTCPKs(pks), + CovenantQuorum: quorum, + SlashingAddress: defaultSlashingAddress(), + MinSlashingTxFeeSat: 1000, + MinCommissionRate: sdkmath.LegacyZeroDec(), + // The Default slashing rate is 0.1 i.e., 10% of the total staked BTC will be burned. + SlashingRate: sdkmath.LegacyNewDecWithPrec(1, 1), // 1 * 10^{-1} = 0.1 + MaxActiveFinalityProviders: defaultMaxActiveFinalityProviders, + // The default minimum unbonding time is 0, which effectively defaults to checkpoint + // finalization timeout. + MinUnbondingTime: 0, + } +} + +// ParamSetPairs get the params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{} +} + +func validateMinSlashingTxFeeSat(fee int64) error { + if fee <= 0 { + return fmt.Errorf("minimum slashing tx fee has to be positive") + } + return nil +} + +func validateMinCommissionRate(rate sdkmath.LegacyDec) error { + if rate.IsNil() { + return fmt.Errorf("minimum commission rate cannot be nil") + } + + if rate.IsNegative() { + return fmt.Errorf("minimum commission rate cannot be negative") + } + + if rate.GT(sdkmath.LegacyOneDec()) { + return fmt.Errorf("minimum commission rate cannot be greater than 100%%") + } + return nil +} + +// validateMaxActiveFinalityProviders checks if the maximum number of +// active finality providers is at least the default value +func validateMaxActiveFinalityProviders(maxActiveFinalityProviders uint32) error { + if maxActiveFinalityProviders == 0 { + return fmt.Errorf("max finality providers must be positive") + } + return nil +} + +// validateCovenantPks checks whether the covenants list contains any duplicates +func validateCovenantPks(covenantPks []bbn.BIP340PubKey) error { + if ExistsDup(covenantPks) { + return fmt.Errorf("duplicate covenant key") + } + return nil +} + +func validateMinUnbondingTime(minUnbondingTimeBlocks uint32) error { + if minUnbondingTimeBlocks > math.MaxUint16 { + return fmt.Errorf("minimum unbonding time blocks cannot be greater than %d", math.MaxUint16) + } + + return nil +} + +// Validate validates the set of params +func (p Params) Validate() error { + if p.CovenantQuorum == 0 { + return fmt.Errorf("covenant quorum size has to be positive") + } + if p.CovenantQuorum*2 <= uint32(len(p.CovenantPks)) { + return fmt.Errorf("covenant quorum size has to be more than 1/2 of the covenant committee size") + } + if err := validateCovenantPks(p.CovenantPks); err != nil { + return err + } + if err := validateMinSlashingTxFeeSat(p.MinSlashingTxFeeSat); err != nil { + return err + } + + if err := validateMinCommissionRate(p.MinCommissionRate); err != nil { + return err + } + + if !btcstaking.IsSlashingRateValid(p.SlashingRate) { + return btcstaking.ErrInvalidSlashingRate + } + + if err := validateMaxActiveFinalityProviders(p.MaxActiveFinalityProviders); err != nil { + return err + } + + if err := validateMinUnbondingTime(p.MinUnbondingTime); err != nil { + return err + } + + return nil +} + +// String implements the Stringer interface. +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} + +func (p Params) HasCovenantPK(pk *bbn.BIP340PubKey) bool { + for _, pk2 := range p.CovenantPks { + if pk2.Equals(pk) { + return true + } + } + return false +} + +func (p Params) MustGetSlashingAddress(btcParams *chaincfg.Params) btcutil.Address { + slashingAddr, err := btcutil.DecodeAddress(p.SlashingAddress, btcParams) + if err != nil { + panic(fmt.Errorf("failed to decode slashing address in genesis: %w", err)) + } + return slashingAddr +} + +func (p Params) CovenantPksHex() []string { + covPksHex := make([]string, 0, len(p.CovenantPks)) + for _, pk := range p.CovenantPks { + covPksHex = append(covPksHex, pk.MarshalHex()) + } + return covPksHex +} diff --git a/x/btcstaking/types/params.pb.go b/x/btcstaking/types/params.pb.go new file mode 100644 index 000000000..642dda4e6 --- /dev/null +++ b/x/btcstaking/types/params.pb.go @@ -0,0 +1,645 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/params.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "github.com/cosmos/cosmos-proto" + _ "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 { + // covenant_pks is the list of public keys held by the covenant committee + // each PK follows encoding in BIP-340 spec on Bitcoin + CovenantPks []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,rep,name=covenant_pks,json=covenantPks,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"covenant_pks,omitempty"` + // covenant_quorum is the minimum number of signatures needed for the covenant + // multisignature + CovenantQuorum uint32 `protobuf:"varint,2,opt,name=covenant_quorum,json=covenantQuorum,proto3" json:"covenant_quorum,omitempty"` + // slashing address is the address that the slashed BTC goes to + // the address is in string on Bitcoin + SlashingAddress string `protobuf:"bytes,3,opt,name=slashing_address,json=slashingAddress,proto3" json:"slashing_address,omitempty"` + // min_slashing_tx_fee_sat is the minimum amount of tx fee (quantified + // in Satoshi) needed for the pre-signed slashing tx + // TODO: change to satoshi per byte? + MinSlashingTxFeeSat int64 `protobuf:"varint,4,opt,name=min_slashing_tx_fee_sat,json=minSlashingTxFeeSat,proto3" json:"min_slashing_tx_fee_sat,omitempty"` + // min_commission_rate is the chain-wide minimum commission rate that a finality provider can charge their delegators + MinCommissionRate cosmossdk_io_math.LegacyDec `protobuf:"bytes,5,opt,name=min_commission_rate,json=minCommissionRate,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"min_commission_rate"` + // slashing_rate determines the portion of the staked amount to be slashed, + // expressed as a decimal (e.g., 0.5 for 50%). + SlashingRate cosmossdk_io_math.LegacyDec `protobuf:"bytes,6,opt,name=slashing_rate,json=slashingRate,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"slashing_rate"` + // max_active_finality_providers is the maximum number of active finality providers in the BTC staking protocol + MaxActiveFinalityProviders uint32 `protobuf:"varint,7,opt,name=max_active_finality_providers,json=maxActiveFinalityProviders,proto3" json:"max_active_finality_providers,omitempty"` + // min_unbonding_time is the minimum time for unbonding transaction timelock in BTC blocks + MinUnbondingTime uint32 `protobuf:"varint,8,opt,name=min_unbonding_time,json=minUnbondingTime,proto3" json:"min_unbonding_time,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_8d1392776a3e15b9, []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) GetCovenantQuorum() uint32 { + if m != nil { + return m.CovenantQuorum + } + return 0 +} + +func (m *Params) GetSlashingAddress() string { + if m != nil { + return m.SlashingAddress + } + return "" +} + +func (m *Params) GetMinSlashingTxFeeSat() int64 { + if m != nil { + return m.MinSlashingTxFeeSat + } + return 0 +} + +func (m *Params) GetMaxActiveFinalityProviders() uint32 { + if m != nil { + return m.MaxActiveFinalityProviders + } + return 0 +} + +func (m *Params) GetMinUnbondingTime() uint32 { + if m != nil { + return m.MinUnbondingTime + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "babylon.btcstaking.v1.Params") +} + +func init() { + proto.RegisterFile("babylon/btcstaking/v1/params.proto", fileDescriptor_8d1392776a3e15b9) +} + +var fileDescriptor_8d1392776a3e15b9 = []byte{ + // 483 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x6f, 0xd3, 0x3c, + 0x18, 0xc7, 0x93, 0xb7, 0x7d, 0x0b, 0x98, 0x8e, 0x8d, 0x00, 0x22, 0x14, 0x91, 0x56, 0xe3, 0x40, + 0x91, 0x20, 0xa1, 0x6c, 0xe2, 0xc0, 0xad, 0x05, 0x4d, 0x42, 0xec, 0x10, 0xd2, 0x81, 0x04, 0x17, + 0xcb, 0x71, 0xbc, 0xd4, 0x6a, 0x6d, 0x97, 0xd8, 0x89, 0x92, 0x6f, 0x01, 0x37, 0x8e, 0x7c, 0x08, + 0x3e, 0xc4, 0x8e, 0x13, 0x27, 0xb4, 0x43, 0x85, 0xda, 0x2f, 0x82, 0xe2, 0x24, 0x85, 0x1b, 0xdc, + 0xe2, 0xff, 0xf3, 0xf3, 0xcf, 0xf1, 0xe3, 0x07, 0xec, 0x87, 0x28, 0x2c, 0x16, 0x82, 0x7b, 0xa1, + 0xc2, 0x52, 0xa1, 0x39, 0xe5, 0xb1, 0x97, 0x8d, 0xbc, 0x25, 0x4a, 0x10, 0x93, 0xee, 0x32, 0x11, + 0x4a, 0x58, 0xb7, 0x6a, 0xc6, 0xfd, 0xcd, 0xb8, 0xd9, 0xa8, 0x77, 0x33, 0x16, 0xb1, 0xd0, 0x84, + 0x57, 0x7e, 0x55, 0x70, 0xef, 0x0e, 0x16, 0x92, 0x09, 0x09, 0xab, 0x42, 0xb5, 0xa8, 0x4a, 0xfb, + 0x9f, 0xdb, 0xa0, 0xe3, 0x6b, 0xb1, 0xf5, 0x1e, 0x74, 0xb1, 0xc8, 0x08, 0x47, 0x5c, 0xc1, 0xe5, + 0x5c, 0xda, 0xe6, 0xa0, 0x35, 0xec, 0x4e, 0x9e, 0x5d, 0xac, 0xfa, 0x4f, 0x63, 0xaa, 0x66, 0x69, + 0xe8, 0x62, 0xc1, 0xbc, 0xfa, 0x5c, 0x3c, 0x43, 0x94, 0x37, 0x0b, 0x4f, 0x15, 0x4b, 0x22, 0xdd, + 0xc9, 0x2b, 0xff, 0xe0, 0xf0, 0x89, 0x9f, 0x86, 0xaf, 0x49, 0x11, 0x5c, 0x6d, 0x5c, 0xfe, 0x5c, + 0x5a, 0x0f, 0xc0, 0xee, 0x56, 0xfd, 0x31, 0x15, 0x49, 0xca, 0xec, 0xff, 0x06, 0xe6, 0x70, 0x27, + 0xb8, 0xd6, 0xc4, 0x6f, 0x74, 0x6a, 0x3d, 0x04, 0x7b, 0x72, 0x81, 0xe4, 0x8c, 0xf2, 0x18, 0xa2, + 0x28, 0x4a, 0x88, 0x94, 0x76, 0x6b, 0x60, 0x0e, 0xaf, 0x04, 0xbb, 0x4d, 0x3e, 0xae, 0x62, 0xeb, + 0x10, 0xdc, 0x66, 0x94, 0xc3, 0x2d, 0xae, 0x72, 0x78, 0x4a, 0x08, 0x94, 0x48, 0xd9, 0xed, 0x81, + 0x39, 0x6c, 0x05, 0x37, 0x18, 0xe5, 0xd3, 0xba, 0x7a, 0x92, 0x1f, 0x11, 0x32, 0x45, 0xca, 0x9a, + 0x82, 0x32, 0x86, 0x58, 0x30, 0x46, 0xa5, 0xa4, 0x82, 0xc3, 0x04, 0x29, 0x62, 0xff, 0x5f, 0x9e, + 0x31, 0xb9, 0x7f, 0xb6, 0xea, 0x1b, 0x17, 0xab, 0xfe, 0xdd, 0xaa, 0x45, 0x32, 0x9a, 0xbb, 0x54, + 0x78, 0x0c, 0xa9, 0x99, 0x7b, 0x4c, 0x62, 0x84, 0x8b, 0x97, 0x04, 0x07, 0xd7, 0x19, 0xe5, 0x2f, + 0xb6, 0xdb, 0x03, 0xa4, 0x88, 0xf5, 0x0e, 0xec, 0x6c, 0x7f, 0x43, 0xeb, 0x3a, 0x5a, 0x37, 0xfa, + 0x07, 0xdd, 0xf7, 0x6f, 0x8f, 0x41, 0xfd, 0x20, 0xa5, 0xbc, 0xdb, 0x78, 0xb4, 0x77, 0x0c, 0xee, + 0x31, 0x94, 0x43, 0x84, 0x15, 0xcd, 0x08, 0x3c, 0xa5, 0x1c, 0x2d, 0xa8, 0x2a, 0xca, 0x67, 0xcc, + 0x68, 0x44, 0x12, 0x69, 0x5f, 0xd2, 0x4d, 0xec, 0x31, 0x94, 0x8f, 0x35, 0x73, 0x54, 0x23, 0x7e, + 0x43, 0x58, 0x8f, 0x80, 0x55, 0xde, 0x37, 0xe5, 0xa1, 0xe0, 0x91, 0x6e, 0x13, 0x65, 0xc4, 0xbe, + 0xac, 0xf7, 0xed, 0x31, 0xca, 0xdf, 0x36, 0x85, 0x13, 0xca, 0xc8, 0xf3, 0xf6, 0x97, 0xaf, 0x7d, + 0x63, 0x72, 0x7c, 0xb6, 0x76, 0xcc, 0xf3, 0xb5, 0x63, 0xfe, 0x5c, 0x3b, 0xe6, 0xa7, 0x8d, 0x63, + 0x9c, 0x6f, 0x1c, 0xe3, 0xc7, 0xc6, 0x31, 0x3e, 0xfc, 0x75, 0x10, 0xf2, 0x3f, 0x67, 0x56, 0x4f, + 0x45, 0xd8, 0xd1, 0x83, 0x76, 0xf0, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xce, 0xa4, 0xfd, 0xd6, + 0x02, 0x00, 0x00, +} + +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.MinUnbondingTime != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MinUnbondingTime)) + i-- + dAtA[i] = 0x40 + } + if m.MaxActiveFinalityProviders != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxActiveFinalityProviders)) + i-- + dAtA[i] = 0x38 + } + { + size := m.SlashingRate.Size() + i -= size + if _, err := m.SlashingRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + { + size := m.MinCommissionRate.Size() + i -= size + if _, err := m.MinCommissionRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.MinSlashingTxFeeSat != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MinSlashingTxFeeSat)) + i-- + dAtA[i] = 0x20 + } + if len(m.SlashingAddress) > 0 { + i -= len(m.SlashingAddress) + copy(dAtA[i:], m.SlashingAddress) + i = encodeVarintParams(dAtA, i, uint64(len(m.SlashingAddress))) + i-- + dAtA[i] = 0x1a + } + if m.CovenantQuorum != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.CovenantQuorum)) + i-- + dAtA[i] = 0x10 + } + if len(m.CovenantPks) > 0 { + for iNdEx := len(m.CovenantPks) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.CovenantPks[iNdEx].Size() + i -= size + if _, err := m.CovenantPks[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + 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 len(m.CovenantPks) > 0 { + for _, e := range m.CovenantPks { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + if m.CovenantQuorum != 0 { + n += 1 + sovParams(uint64(m.CovenantQuorum)) + } + l = len(m.SlashingAddress) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if m.MinSlashingTxFeeSat != 0 { + n += 1 + sovParams(uint64(m.MinSlashingTxFeeSat)) + } + l = m.MinCommissionRate.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.SlashingRate.Size() + n += 1 + l + sovParams(uint64(l)) + if m.MaxActiveFinalityProviders != 0 { + n += 1 + sovParams(uint64(m.MaxActiveFinalityProviders)) + } + if m.MinUnbondingTime != 0 { + n += 1 + sovParams(uint64(m.MinUnbondingTime)) + } + 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantPks", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.CovenantPks = append(m.CovenantPks, v) + if err := m.CovenantPks[len(m.CovenantPks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantQuorum", wireType) + } + m.CovenantQuorum = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CovenantQuorum |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SlashingAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinSlashingTxFeeSat", wireType) + } + m.MinSlashingTxFeeSat = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinSlashingTxFeeSat |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinCommissionRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinCommissionRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SlashingRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxActiveFinalityProviders", wireType) + } + m.MaxActiveFinalityProviders = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxActiveFinalityProviders |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinUnbondingTime", wireType) + } + m.MinUnbondingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinUnbondingTime |= 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/btcstaking/types/pop.go b/x/btcstaking/types/pop.go new file mode 100644 index 000000000..1c0521661 --- /dev/null +++ b/x/btcstaking/types/pop.go @@ -0,0 +1,442 @@ +package types + +import ( + "bytes" + "encoding/hex" + "fmt" + + "github.com/babylonchain/babylon/crypto/bip322" + "github.com/babylonchain/babylon/crypto/ecdsa" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/cometbft/cometbft/crypto/tmhash" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +type checkStakerKey func(stakerKey *bbn.BIP340PubKey) error + +type bip322Sign[A btcutil.Address] func(sg []byte, + privKey *btcec.PrivateKey, + net *chaincfg.Params) (A, []byte, error) + +// NewPoP generates a new proof of possession that sk_Babylon and sk_BTC are held by the same person +// a proof of possession contains two signatures: +// - pop.BabylonSig = sign(sk_Babylon, pk_BTC) +// - pop.BtcSig = schnorr_sign(sk_BTC, pop.BabylonSig) +func NewPoP(babylonSK cryptotypes.PrivKey, btcSK *btcec.PrivateKey) (*ProofOfPossession, error) { + pop := ProofOfPossession{ + BtcSigType: BTCSigType_BIP340, // by default, we use BIP-340 encoding for BTC signature + } + + // generate pop.BabylonSig = sign(sk_Babylon, pk_BTC) + btcPK := btcSK.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + babylonSig, err := babylonSK.Sign(*bip340PK) + if err != nil { + return nil, err + } + pop.BabylonSig = babylonSig + + // generate pop.BtcSig = schnorr_sign(sk_BTC, pop.BabylonSig) + // NOTE: *schnorr.Sign has to take the hash of the message. + // So we have to hash babylonSig before signing + babylonSigHash := tmhash.Sum(pop.BabylonSig) + btcSig, err := schnorr.Sign(btcSK, babylonSigHash) + if err != nil { + return nil, err + } + bip340Sig := bbn.NewBIP340SignatureFromBTCSig(btcSig) + pop.BtcSig = bip340Sig.MustMarshal() + + return &pop, nil +} + +// NewPoPWithECDSABTCSig generates a new proof of possession where Bitcoin signature is in ECDSA format +// a proof of possession contains two signatures: +// - pop.BabylonSig = sign(sk_Babylon, pk_BTC) +// - pop.BtcSig = ecdsa_sign(sk_BTC, pop.BabylonSig) +func NewPoPWithECDSABTCSig(babylonSK cryptotypes.PrivKey, btcSK *btcec.PrivateKey) (*ProofOfPossession, error) { + pop := ProofOfPossession{ + BtcSigType: BTCSigType_ECDSA, + } + + // generate pop.BabylonSig = sign(sk_Babylon, pk_BTC) + btcPK := btcSK.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + babylonSig, err := babylonSK.Sign(*bip340PK) + if err != nil { + return nil, err + } + pop.BabylonSig = babylonSig + + // generate pop.BtcSig = ecdsa_sign(sk_BTC, pop.BabylonSig) + // NOTE: ecdsa.Sign has to take the message as string. + // So we have to hex babylonSig before signing + babylonSigHex := hex.EncodeToString(pop.BabylonSig) + btcSig, err := ecdsa.Sign(btcSK, babylonSigHex) + if err != nil { + return nil, err + } + pop.BtcSig = btcSig + + return &pop, nil +} + +func babylonSigToHexHash(babylonSig []byte) []byte { + babylonSigHash := tmhash.Sum(babylonSig) + babylonSigHashHex := hex.EncodeToString(babylonSigHash) + babylonSigHashHexBytes := []byte(babylonSigHashHex) + return babylonSigHashHexBytes +} + +func newPoPWithBIP322Sig[A btcutil.Address]( + babylonSK cryptotypes.PrivKey, + btcSK *btcec.PrivateKey, + net *chaincfg.Params, + bip322SignFn bip322Sign[A], +) (*ProofOfPossession, error) { + pop := ProofOfPossession{ + BtcSigType: BTCSigType_BIP322, + } + + // generate pop.BabylonSig = sign(sk_Babylon, pk_BTC) + btcPK := btcSK.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + babylonSig, err := babylonSK.Sign(*bip340PK) + if err != nil { + return nil, err + } + pop.BabylonSig = babylonSig + + // TODO: temporary solution for MVP purposes. + // Eventually we need to use tmhash.Sum(pop.BabylonSig) rather than bbnSigHashHexBytes + // ref: https://github.com/babylonchain/babylon/issues/433 + bbnSigHashHexBytes := babylonSigToHexHash(pop.BabylonSig) + + address, witnessSignture, err := bip322SignFn( + bbnSigHashHexBytes, + btcSK, + net, + ) + + if err != nil { + return nil, err + } + + bip322Sig := BIP322Sig{ + Address: address.EncodeAddress(), + Sig: witnessSignture, + } + + bip322SigEncoded, err := bip322Sig.Marshal() + + if err != nil { + return nil, err + } + pop.BtcSig = bip322SigEncoded + + return &pop, nil +} + +func NewPoPWithBIP322P2WPKHSig( + babylonSK cryptotypes.PrivKey, + btcSK *btcec.PrivateKey, + net *chaincfg.Params, +) (*ProofOfPossession, error) { + return newPoPWithBIP322Sig(babylonSK, btcSK, net, bip322.SignWithP2WPKHAddress) +} + +func NewPoPWithBIP322P2TRBIP86Sig( + babylonSK cryptotypes.PrivKey, + btcSK *btcec.PrivateKey, + net *chaincfg.Params, +) (*ProofOfPossession, error) { + return newPoPWithBIP322Sig(babylonSK, btcSK, net, bip322.SignWithP2TrSpendAddress) +} + +func NewPoPFromHex(popHex string) (*ProofOfPossession, error) { + popBytes, err := hex.DecodeString(popHex) + if err != nil { + return nil, err + } + var pop ProofOfPossession + if err := pop.Unmarshal(popBytes); err != nil { + return nil, err + } + return &pop, nil +} + +func (pop *ProofOfPossession) ToHexStr() (string, error) { + popBytes, err := pop.Marshal() + if err != nil { + return "", err + } + return hex.EncodeToString(popBytes), nil +} + +func (pop *ProofOfPossession) Verify(babylonPK cryptotypes.PubKey, bip340PK *bbn.BIP340PubKey, net *chaincfg.Params) error { + switch pop.BtcSigType { + case BTCSigType_BIP340: + return pop.VerifyBIP340(babylonPK, bip340PK) + case BTCSigType_BIP322: + return pop.VerifyBIP322(babylonPK, bip340PK, net) + case BTCSigType_ECDSA: + return pop.VerifyECDSA(babylonPK, bip340PK) + default: + return fmt.Errorf("invalid BTC signature type") + } +} + +// VerifyBIP340 verifies the validity of PoP where Bitcoin signature is in BIP-340 +// 1. verify(sig=sig_btc, pubkey=pk_btc, msg=pop.BabylonSig)? +// 2. verify(sig=pop.BabylonSig, pubkey=pk_babylon, msg=pk_btc)? +func (pop *ProofOfPossession) VerifyBIP340(babylonPK cryptotypes.PubKey, bip340PK *bbn.BIP340PubKey) error { + if pop.BtcSigType != BTCSigType_BIP340 { + return fmt.Errorf("the Bitcoin signature in this proof of possession is not using BIP-340 encoding") + } + + // rule 1: verify(sig=sig_btc, pubkey=pk_btc, msg=pop.BabylonSig)? + bip340Sig, err := bbn.NewBIP340Signature(pop.BtcSig) + if err != nil { + return err + } + btcSig, err := bip340Sig.ToBTCSig() + if err != nil { + return err + } + btcPK, err := bip340PK.ToBTCPK() + if err != nil { + return err + } + // NOTE: btcSig.Verify has to take hash of the message. + // So we have to hash babylonSig before verifying the signature + babylonSigHash := tmhash.Sum(pop.BabylonSig) + if !btcSig.Verify(babylonSigHash, btcPK) { + return fmt.Errorf("failed to verify pop.BtcSig") + } + + // rule 2: verify(sig=pop.BabylonSig, pubkey=pk_babylon, msg=pk_btc)? + if !babylonPK.VerifySignature(*bip340PK, pop.BabylonSig) { + return fmt.Errorf("failed to verify pop.BabylonSig") + } + + return nil +} + +// isSupportedAddressAndWitness checks whether provided address and witness are +// valid for proof of possession verification. +// Currently the only supported options are: +// 1. p2wpkh address which should only 2 elements in witness: signature and public key +// 2. p2tr address which should only 1 element in witness: signature i.e p2tr key spend +// If validation succeeds, it returns a function which can be used to check whether +// bip340PK corresponds to verified address. +func isSupportedAddressAndWitness( + address btcutil.Address, + witness wire.TxWitness, + net *chaincfg.Params) (checkStakerKey, error) { + script, err := txscript.PayToAddrScript(address) + + if err != nil { + return nil, err + } + + // pay to taproot key spending path have only signature in witness + if txscript.IsPayToTaproot(script) && len(witness) == 1 { + return func(stakerKey *bbn.BIP340PubKey) error { + btcKey, err := stakerKey.ToBTCPK() + + if err != nil { + return err + } + + keyAddress, err := bip322.PubKeyToP2TrSpendAddress(btcKey, net) + + if err != nil { + return err + } + + if !bytes.Equal(keyAddress.ScriptAddress(), address.ScriptAddress()) { + return fmt.Errorf("bip322Sig.Address does not correspond to bip340PK") + } + + return nil + }, nil + } + + // pay to witness key hash have signature and public key in witness + if txscript.IsPayToWitnessPubKeyHash(script) && len(witness) == 2 { + return func(stakerKey *bbn.BIP340PubKey) error { + keyFromWitness, err := btcec.ParsePubKey(witness[1]) + + if err != nil { + return err + } + + keyFromWitnessBytess := schnorr.SerializePubKey(keyFromWitness) + + stakerKeyEncoded, err := stakerKey.Marshal() + + if err != nil { + return err + } + + if !bytes.Equal(keyFromWitnessBytess, stakerKeyEncoded) { + return fmt.Errorf("bip322Sig.Address does not correspond to bip340PK") + } + + return nil + + }, nil + } + + return nil, fmt.Errorf("unsupported bip322 address type. Only supported options are p2wpkh and p2tr bip86 key spending path") +} + +// VerifyBIP322SigPop verifies bip322 `signature` over `msg` and also checks whether +// `address` corresponds to `pubKeyNoCoord` in the given network. +// It supports only two type of addresses: +// 1. p2wpkh address +// 2. p2tr address which is defined in bip88 +// Parameters: +// - msg: message which was signed +// - address: address which was used to sign the message +// - signature: bip322 signature over the message +// - pubKeyNoCoord: public key in 32 bytes format which was used to derive address +func VerifyBIP322SigPop( + msg []byte, + address string, + signature []byte, + pubKeyNoCoord []byte, + net *chaincfg.Params, +) error { + if len(msg) == 0 || len(address) == 0 || len(signature) == 0 || len(pubKeyNoCoord) == 0 { + return fmt.Errorf("cannot verfiy bip322 signature. One of the required parameters is empty") + } + + witness, err := bip322.SimpleSigToWitness(signature) + if err != nil { + return err + } + + btcAddress, err := btcutil.DecodeAddress(address, net) + if err != nil { + return err + } + + // we check whether address and witness are valid for proof of possession verification + // before verifying bip322 signature. This is require to avoid cases in which + // we receive some long running btc script to execute (like taproot script with 100 signatures) + // for proof of possession, we only support two types of cases: + // 1. address is p2wpkh address + // 2. address is p2tr address and we are dealing with bip86 (https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki) + // key spending path. + // In those two cases we are able to link bip340PK public key to the btc address + // used in bip322 signature verification. + stakerKeyMatchesBtcAddressFn, err := isSupportedAddressAndWitness(btcAddress, witness, net) + if err != nil { + return err + } + + if err := bip322.Verify(msg, witness, btcAddress, net); err != nil { + return err + } + + key, err := bbn.NewBIP340PubKey(pubKeyNoCoord) + if err != nil { + return err + } + + // rule 3: verify bip322Sig.Address corresponds to bip340PK + if err := stakerKeyMatchesBtcAddressFn(key); err != nil { + return err + } + + return nil +} + +// VerifyBIP322 verifies the validity of PoP where Bitcoin signature is in BIP-322 +// after decoding pop.BtcSig to bip322Sig which contains sig and address, +// 1. verify whether bip322 pop signature where msg=pop.BabylonSig +// 2. verify(sig=pop.BabylonSig, pubkey=babylonPK, msg=bip340PK)? +func (pop *ProofOfPossession) VerifyBIP322(babylonPK cryptotypes.PubKey, bip340PK *bbn.BIP340PubKey, net *chaincfg.Params) error { + if pop.BtcSigType != BTCSigType_BIP322 { + return fmt.Errorf("the Bitcoin signature in this proof of possession is not using BIP-322 encoding") + } + // unmarshal pop.BtcSig to bip322Sig + var bip322Sig BIP322Sig + if err := bip322Sig.Unmarshal(pop.BtcSig); err != nil { + return nil + } + + // TODO: temporary solution for MVP purposes. + // Eventually we need to use tmhash.Sum(pop.BabylonSig) rather than bbnSigHashHexBytes + // ref: https://github.com/babylonchain/babylon/issues/433 + bbnSigHashHexBytes := babylonSigToHexHash(pop.BabylonSig) + + btcKeyBytes, err := bip340PK.Marshal() + if err != nil { + return err + } + + // 1. Verify Bip322 proof of possession signature + if err := VerifyBIP322SigPop( + bbnSigHashHexBytes, + bip322Sig.Address, + bip322Sig.Sig, + btcKeyBytes, + net, + ); err != nil { + return err + } + + // rule 2: verify(sig=pop.BabylonSig, pubkey=pk_babylon, msg=pk_btc)? + if !babylonPK.VerifySignature(*bip340PK, pop.BabylonSig) { + return fmt.Errorf("failed to verify pop.BabylonSig") + } + + return nil +} + +// VerifyECDSA verifies the validity of PoP where Bitcoin signature is in ECDSA encoding +// 1. verify(sig=sig_btc, pubkey=pk_btc, msg=pop.BabylonSig)? +// 2. verify(sig=pop.BabylonSig, pubkey=pk_babylon, msg=pk_btc)? +func (pop *ProofOfPossession) VerifyECDSA(babylonPK cryptotypes.PubKey, bip340PK *bbn.BIP340PubKey) error { + if pop.BtcSigType != BTCSigType_ECDSA { + return fmt.Errorf("the Bitcoin signature in this proof of possession is not using ECDSA encoding") + } + + // rule 1: verify(sig=sig_btc, pubkey=pk_btc, msg=pop.BabylonSig)? + btcPK, err := bip340PK.ToBTCPK() + if err != nil { + return err + } + // NOTE: ecdsa.Verify has to take message as a string + // So we have to hex BabylonSig before verifying the signature + bbnSigHex := hex.EncodeToString(pop.BabylonSig) + if err := ecdsa.Verify(btcPK, bbnSigHex, pop.BtcSig); err != nil { + return fmt.Errorf("failed to verify pop.BtcSig") + } + + // rule 2: verify(sig=pop.BabylonSig, pubkey=pk_babylon, msg=pk_btc)? + if !babylonPK.VerifySignature(*bip340PK, pop.BabylonSig) { + return fmt.Errorf("failed to verify pop.BabylonSig") + } + + return nil +} + +func (pop *ProofOfPossession) ValidateBasic() error { + if len(pop.BabylonSig) == 0 { + return fmt.Errorf("empty Babylon signature") + } + if pop.BtcSig == nil { + return fmt.Errorf("empty BTC signature") + } + + return nil +} diff --git a/x/btcstaking/types/pop.pb.go b/x/btcstaking/types/pop.pb.go new file mode 100644 index 000000000..672a3f88a --- /dev/null +++ b/x/btcstaking/types/pop.pb.go @@ -0,0 +1,681 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/pop.proto + +package types + +import ( + fmt "fmt" + 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 + +// BTCSigType indicates the type of btc_sig in a pop +type BTCSigType int32 + +const ( + // BIP340 means the btc_sig will follow the BIP-340 encoding + BTCSigType_BIP340 BTCSigType = 0 + // BIP322 means the btc_sig will follow the BIP-322 encoding + BTCSigType_BIP322 BTCSigType = 1 + // ECDSA means the btc_sig will follow the ECDSA encoding + // ref: https://github.com/okx/js-wallet-sdk/blob/a57c2acbe6ce917c0aa4e951d96c4e562ad58444/packages/coin-bitcoin/src/BtcWallet.ts#L331 + BTCSigType_ECDSA BTCSigType = 2 +) + +var BTCSigType_name = map[int32]string{ + 0: "BIP340", + 1: "BIP322", + 2: "ECDSA", +} + +var BTCSigType_value = map[string]int32{ + "BIP340": 0, + "BIP322": 1, + "ECDSA": 2, +} + +func (x BTCSigType) String() string { + return proto.EnumName(BTCSigType_name, int32(x)) +} + +func (BTCSigType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_9d6ceb088d9e9f3a, []int{0} +} + +// ProofOfPossession is the proof of possession that a Babylon secp256k1 +// secret key and a Bitcoin secp256k1 secret key are held by the same +// person +type ProofOfPossession struct { + // btc_sig_type indicates the type of btc_sig in the pop + BtcSigType BTCSigType `protobuf:"varint,1,opt,name=btc_sig_type,json=btcSigType,proto3,enum=babylon.btcstaking.v1.BTCSigType" json:"btc_sig_type,omitempty"` + // babylon_sig is the signature generated via sign(sk_babylon, pk_btc) + BabylonSig []byte `protobuf:"bytes,2,opt,name=babylon_sig,json=babylonSig,proto3" json:"babylon_sig,omitempty"` + // btc_sig is the signature generated via sign(sk_btc, babylon_sig) + // the signature follows encoding in either BIP-340 spec or BIP-322 spec + BtcSig []byte `protobuf:"bytes,3,opt,name=btc_sig,json=btcSig,proto3" json:"btc_sig,omitempty"` +} + +func (m *ProofOfPossession) Reset() { *m = ProofOfPossession{} } +func (m *ProofOfPossession) String() string { return proto.CompactTextString(m) } +func (*ProofOfPossession) ProtoMessage() {} +func (*ProofOfPossession) Descriptor() ([]byte, []int) { + return fileDescriptor_9d6ceb088d9e9f3a, []int{0} +} +func (m *ProofOfPossession) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProofOfPossession) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProofOfPossession.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 *ProofOfPossession) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProofOfPossession.Merge(m, src) +} +func (m *ProofOfPossession) XXX_Size() int { + return m.Size() +} +func (m *ProofOfPossession) XXX_DiscardUnknown() { + xxx_messageInfo_ProofOfPossession.DiscardUnknown(m) +} + +var xxx_messageInfo_ProofOfPossession proto.InternalMessageInfo + +func (m *ProofOfPossession) GetBtcSigType() BTCSigType { + if m != nil { + return m.BtcSigType + } + return BTCSigType_BIP340 +} + +func (m *ProofOfPossession) GetBabylonSig() []byte { + if m != nil { + return m.BabylonSig + } + return nil +} + +func (m *ProofOfPossession) GetBtcSig() []byte { + if m != nil { + return m.BtcSig + } + return nil +} + +// BIP322Sig is a BIP-322 signature together with the address corresponding to +// the signer +type BIP322Sig struct { + // address is the signer's address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // sig is the actual signature in BIP-322 format + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` +} + +func (m *BIP322Sig) Reset() { *m = BIP322Sig{} } +func (m *BIP322Sig) String() string { return proto.CompactTextString(m) } +func (*BIP322Sig) ProtoMessage() {} +func (*BIP322Sig) Descriptor() ([]byte, []int) { + return fileDescriptor_9d6ceb088d9e9f3a, []int{1} +} +func (m *BIP322Sig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BIP322Sig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BIP322Sig.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 *BIP322Sig) XXX_Merge(src proto.Message) { + xxx_messageInfo_BIP322Sig.Merge(m, src) +} +func (m *BIP322Sig) XXX_Size() int { + return m.Size() +} +func (m *BIP322Sig) XXX_DiscardUnknown() { + xxx_messageInfo_BIP322Sig.DiscardUnknown(m) +} + +var xxx_messageInfo_BIP322Sig proto.InternalMessageInfo + +func (m *BIP322Sig) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *BIP322Sig) GetSig() []byte { + if m != nil { + return m.Sig + } + return nil +} + +func init() { + proto.RegisterEnum("babylon.btcstaking.v1.BTCSigType", BTCSigType_name, BTCSigType_value) + proto.RegisterType((*ProofOfPossession)(nil), "babylon.btcstaking.v1.ProofOfPossession") + proto.RegisterType((*BIP322Sig)(nil), "babylon.btcstaking.v1.BIP322Sig") +} + +func init() { proto.RegisterFile("babylon/btcstaking/v1/pop.proto", fileDescriptor_9d6ceb088d9e9f3a) } + +var fileDescriptor_9d6ceb088d9e9f3a = []byte{ + // 298 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4f, 0xc2, 0x30, + 0x1c, 0xc5, 0x57, 0x88, 0x10, 0xfe, 0x12, 0x33, 0x9b, 0x18, 0x77, 0x2a, 0xc8, 0x89, 0x78, 0x68, + 0xa5, 0x98, 0x78, 0x16, 0xf4, 0x60, 0x62, 0x22, 0x01, 0x4e, 0x5e, 0xc8, 0x3a, 0xc6, 0x68, 0xd4, + 0x75, 0xa1, 0x95, 0xc8, 0xb7, 0x30, 0x7e, 0x2a, 0x8f, 0x1c, 0x3d, 0x9a, 0xed, 0x8b, 0x98, 0xcd, + 0x2e, 0xf3, 0xe0, 0xed, 0xbd, 0xf6, 0xf5, 0xd7, 0x97, 0x07, 0x1d, 0xe1, 0x8b, 0xdd, 0xb3, 0x8a, + 0x99, 0x30, 0x81, 0x36, 0xfe, 0x93, 0x8c, 0x23, 0xb6, 0x1d, 0xb0, 0x44, 0x25, 0x34, 0xd9, 0x28, + 0xa3, 0xf0, 0x89, 0x0d, 0xd0, 0x2a, 0x40, 0xb7, 0x83, 0xde, 0x07, 0x82, 0xe3, 0xc9, 0x46, 0xa9, + 0xd5, 0xc3, 0x6a, 0xa2, 0xb4, 0x0e, 0xb5, 0x96, 0x2a, 0xc6, 0x63, 0x68, 0x0b, 0x13, 0x2c, 0xb4, + 0x8c, 0x16, 0x66, 0x97, 0x84, 0x1e, 0xea, 0xa2, 0xfe, 0x11, 0x3f, 0xa3, 0xff, 0x32, 0xe8, 0x68, + 0x3e, 0x9e, 0xc9, 0x68, 0xbe, 0x4b, 0xc2, 0x29, 0x08, 0x13, 0x58, 0x8d, 0x3b, 0x70, 0x68, 0xf3, + 0x39, 0xc8, 0xab, 0x75, 0x51, 0xbf, 0x3d, 0x05, 0x7b, 0x34, 0x93, 0x11, 0x3e, 0x85, 0xa6, 0xfd, + 0xc5, 0xab, 0x17, 0x97, 0x8d, 0xdf, 0xd7, 0xbd, 0x2b, 0x68, 0x8d, 0xee, 0x26, 0x43, 0xce, 0xf3, + 0x94, 0x07, 0x4d, 0x7f, 0xb9, 0xdc, 0x84, 0x5a, 0x17, 0x35, 0x5a, 0xd3, 0xd2, 0x62, 0x17, 0xea, + 0x15, 0x38, 0x97, 0xe7, 0x0c, 0xa0, 0x2a, 0x83, 0x01, 0x1a, 0x39, 0xe6, 0xf2, 0xc2, 0x75, 0x4a, + 0xcd, 0xb9, 0x8b, 0x70, 0x0b, 0x0e, 0x6e, 0xc7, 0x37, 0xb3, 0x6b, 0xb7, 0x36, 0xba, 0xff, 0x4c, + 0x09, 0xda, 0xa7, 0x04, 0x7d, 0xa7, 0x04, 0xbd, 0x67, 0xc4, 0xd9, 0x67, 0xc4, 0xf9, 0xca, 0x88, + 0xf3, 0xc8, 0x23, 0x69, 0xd6, 0xaf, 0x82, 0x06, 0xea, 0x85, 0xd9, 0xce, 0xc1, 0xda, 0x97, 0x71, + 0x69, 0xd8, 0xdb, 0xdf, 0xa9, 0xf3, 0x95, 0xb4, 0x68, 0x14, 0x53, 0x0f, 0x7f, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x89, 0x18, 0xe3, 0x67, 0x8d, 0x01, 0x00, 0x00, +} + +func (m *ProofOfPossession) 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 *ProofOfPossession) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProofOfPossession) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BtcSig) > 0 { + i -= len(m.BtcSig) + copy(dAtA[i:], m.BtcSig) + i = encodeVarintPop(dAtA, i, uint64(len(m.BtcSig))) + i-- + dAtA[i] = 0x1a + } + if len(m.BabylonSig) > 0 { + i -= len(m.BabylonSig) + copy(dAtA[i:], m.BabylonSig) + i = encodeVarintPop(dAtA, i, uint64(len(m.BabylonSig))) + i-- + dAtA[i] = 0x12 + } + if m.BtcSigType != 0 { + i = encodeVarintPop(dAtA, i, uint64(m.BtcSigType)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BIP322Sig) 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 *BIP322Sig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BIP322Sig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Sig) > 0 { + i -= len(m.Sig) + copy(dAtA[i:], m.Sig) + i = encodeVarintPop(dAtA, i, uint64(len(m.Sig))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintPop(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintPop(dAtA []byte, offset int, v uint64) int { + offset -= sovPop(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ProofOfPossession) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcSigType != 0 { + n += 1 + sovPop(uint64(m.BtcSigType)) + } + l = len(m.BabylonSig) + if l > 0 { + n += 1 + l + sovPop(uint64(l)) + } + l = len(m.BtcSig) + if l > 0 { + n += 1 + l + sovPop(uint64(l)) + } + return n +} + +func (m *BIP322Sig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovPop(uint64(l)) + } + l = len(m.Sig) + if l > 0 { + n += 1 + l + sovPop(uint64(l)) + } + return n +} + +func sovPop(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPop(x uint64) (n int) { + return sovPop(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ProofOfPossession) 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 ErrIntOverflowPop + } + 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: ProofOfPossession: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProofOfPossession: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcSigType", wireType) + } + m.BtcSigType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BtcSigType |= BTCSigType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPop + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BabylonSig = append(m.BabylonSig[:0], dAtA[iNdEx:postIndex]...) + if m.BabylonSig == nil { + m.BabylonSig = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPop + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BtcSig = append(m.BtcSig[:0], dAtA[iNdEx:postIndex]...) + if m.BtcSig == nil { + m.BtcSig = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPop(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPop + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BIP322Sig) 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 ErrIntOverflowPop + } + 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: BIP322Sig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BIP322Sig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPop + } + 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 ErrInvalidLengthPop + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPop + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPop + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPop + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sig = append(m.Sig[:0], dAtA[iNdEx:postIndex]...) + if m.Sig == nil { + m.Sig = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPop(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPop + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPop(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, ErrIntOverflowPop + } + 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, ErrIntOverflowPop + } + 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, ErrIntOverflowPop + } + 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, ErrInvalidLengthPop + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPop + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPop + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPop = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPop = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPop = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/pop_test.go b/x/btcstaking/types/pop_test.go new file mode 100644 index 000000000..34fa89d6f --- /dev/null +++ b/x/btcstaking/types/pop_test.go @@ -0,0 +1,185 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg" + "github.com/cometbft/cometbft/crypto/tmhash" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/stretchr/testify/require" +) + +var ( + net = &chaincfg.TestNet3Params +) + +func newInvalidBIP340PoP(r *rand.Rand, babylonSK cryptotypes.PrivKey, btcSK *btcec.PrivateKey) *types.ProofOfPossession { + pop := types.ProofOfPossession{} + + randomNum := datagen.RandomInt(r, 2) // 0 or 1 + + btcPK := btcSK.PubKey() + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + babylonSig, err := babylonSK.Sign(*bip340PK) + if err != nil { + panic(err) + } + + var babylonSigHash []byte + if randomNum == 0 { + pop.BabylonSig = babylonSig // correct sig + babylonSigHash = datagen.GenRandomByteArray(r, 32) // fake sig hash + } else { + pop.BabylonSig = datagen.GenRandomByteArray(r, uint64(len(babylonSig))) // fake sig + babylonSigHash = tmhash.Sum(pop.BabylonSig) // correct sig hash + } + + btcSig, err := schnorr.Sign(btcSK, babylonSigHash) + if err != nil { + panic(err) + } + bip340Sig := bbn.NewBIP340SignatureFromBTCSig(btcSig) + pop.BtcSig = bip340Sig.MustMarshal() + + return &pop +} + +func FuzzPoP_BIP340(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate BTC key pair + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + + // generate Babylon key pair + babylonSK, babylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + + // generate and verify PoP, correct case + pop, err := types.NewPoP(babylonSK, btcSK) + require.NoError(t, err) + err = pop.VerifyBIP340(babylonPK, bip340PK) + require.NoError(t, err) + + // generate and verify PoP, invalid case + invalidPoP := newInvalidBIP340PoP(r, babylonSK, btcSK) + err = invalidPoP.VerifyBIP340(babylonPK, bip340PK) + require.Error(t, err) + }) +} + +func FuzzPoP_ECDSA(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate BTC key pair + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + + // generate Babylon key pair + babylonSK, babylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + + // generate and verify PoP, correct case + pop, err := types.NewPoPWithECDSABTCSig(babylonSK, btcSK) + require.NoError(t, err) + err = pop.VerifyECDSA(babylonPK, bip340PK) + require.NoError(t, err) + }) +} + +func FuzzPoP_BIP322_P2WPKH(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate BTC key pair + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + + // generate Babylon key pair + babylonSK, babylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + + // generate and verify PoP, correct case + pop, err := types.NewPoPWithBIP322P2WPKHSig(babylonSK, btcSK, net) + require.NoError(t, err) + err = pop.VerifyBIP322(babylonPK, bip340PK, net) + require.NoError(t, err) + }) +} + +func FuzzPoP_BIP322_P2Tr_BIP86(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate BTC key pair + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + + // generate Babylon key pair + babylonSK, babylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + + // generate and verify PoP, correct case + pop, err := types.NewPoPWithBIP322P2TRBIP86Sig(babylonSK, btcSK, net) + require.NoError(t, err) + err = pop.VerifyBIP322(babylonPK, bip340PK, net) + require.NoError(t, err) + }) +} + +// TODO: Add more negative cases +func FuzzPop_ValidBip322SigNotMatchingBip340PubKey(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate two BTC key pairs + btcSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + _, btcPK1, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + bip340PK1 := bbn.NewBIP340PubKeyFromBTCPK(btcPK1) + + // generate Babylon key pair + babylonSK, babylonPK, err := datagen.GenRandomSecp256k1KeyPair(r) + require.NoError(t, err) + + // generate valid bip322 P2WPKH pop + pop, err := types.NewPoPWithBIP322P2WPKHSig(babylonSK, btcSK, net) + require.NoError(t, err) + + // verify bip322 pop with incorrect staker key + err = pop.VerifyBIP322(babylonPK, bip340PK1, net) + require.Error(t, err) + + // generate valid bip322 P2Tr pop + pop, err = types.NewPoPWithBIP322P2TRBIP86Sig(babylonSK, btcSK, net) + require.NoError(t, err) + + // verify bip322 pop with incorrect staker key + err = pop.VerifyBIP322(babylonPK, bip340PK1, net) + require.Error(t, err) + }) +} diff --git a/x/btcstaking/types/query.pb.go b/x/btcstaking/types/query.pb.go new file mode 100644 index 000000000..fb4f4e200 --- /dev/null +++ b/x/btcstaking/types/query.pb.go @@ -0,0 +1,4945 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/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" + 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. +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 + +// QueryParamsRequest is 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_74d49d26f7429697, []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 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_74d49d26f7429697, []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{} +} + +// QueryFinalityProvidersRequest is the request type for the +// Query/FinalityProviders RPC method. +type QueryFinalityProvidersRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFinalityProvidersRequest) Reset() { *m = QueryFinalityProvidersRequest{} } +func (m *QueryFinalityProvidersRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProvidersRequest) ProtoMessage() {} +func (*QueryFinalityProvidersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{2} +} +func (m *QueryFinalityProvidersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProvidersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProvidersRequest.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 *QueryFinalityProvidersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProvidersRequest.Merge(m, src) +} +func (m *QueryFinalityProvidersRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProvidersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProvidersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProvidersRequest proto.InternalMessageInfo + +func (m *QueryFinalityProvidersRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryFinalityProvidersResponse is the response type for the +// Query/FinalityProviders RPC method. +type QueryFinalityProvidersResponse struct { + // finality_providers contains all the finality providers + FinalityProviders []*FinalityProvider `protobuf:"bytes,1,rep,name=finality_providers,json=finalityProviders,proto3" json:"finality_providers,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFinalityProvidersResponse) Reset() { *m = QueryFinalityProvidersResponse{} } +func (m *QueryFinalityProvidersResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProvidersResponse) ProtoMessage() {} +func (*QueryFinalityProvidersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{3} +} +func (m *QueryFinalityProvidersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProvidersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProvidersResponse.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 *QueryFinalityProvidersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProvidersResponse.Merge(m, src) +} +func (m *QueryFinalityProvidersResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProvidersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProvidersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProvidersResponse proto.InternalMessageInfo + +func (m *QueryFinalityProvidersResponse) GetFinalityProviders() []*FinalityProvider { + if m != nil { + return m.FinalityProviders + } + return nil +} + +func (m *QueryFinalityProvidersResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryFinalityProviderRequest requests information about a finality provider +type QueryFinalityProviderRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` +} + +func (m *QueryFinalityProviderRequest) Reset() { *m = QueryFinalityProviderRequest{} } +func (m *QueryFinalityProviderRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProviderRequest) ProtoMessage() {} +func (*QueryFinalityProviderRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{4} +} +func (m *QueryFinalityProviderRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderRequest.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 *QueryFinalityProviderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderRequest.Merge(m, src) +} +func (m *QueryFinalityProviderRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderRequest proto.InternalMessageInfo + +func (m *QueryFinalityProviderRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +// QueryFinalityProviderResponse contains information about a finality provider +type QueryFinalityProviderResponse struct { + // finality_provider contains the FinalityProvider + FinalityProvider *FinalityProvider `protobuf:"bytes,1,opt,name=finality_provider,json=finalityProvider,proto3" json:"finality_provider,omitempty"` +} + +func (m *QueryFinalityProviderResponse) Reset() { *m = QueryFinalityProviderResponse{} } +func (m *QueryFinalityProviderResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProviderResponse) ProtoMessage() {} +func (*QueryFinalityProviderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{5} +} +func (m *QueryFinalityProviderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderResponse.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 *QueryFinalityProviderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderResponse.Merge(m, src) +} +func (m *QueryFinalityProviderResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderResponse proto.InternalMessageInfo + +func (m *QueryFinalityProviderResponse) GetFinalityProvider() *FinalityProvider { + if m != nil { + return m.FinalityProvider + } + return nil +} + +// QueryBTCDelegationsRequest is the request type for the +// Query/BTCDelegations RPC method. +type QueryBTCDelegationsRequest struct { + // status is the queried status for BTC delegations + Status BTCDelegationStatus `protobuf:"varint,1,opt,name=status,proto3,enum=babylon.btcstaking.v1.BTCDelegationStatus" json:"status,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryBTCDelegationsRequest) Reset() { *m = QueryBTCDelegationsRequest{} } +func (m *QueryBTCDelegationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBTCDelegationsRequest) ProtoMessage() {} +func (*QueryBTCDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{6} +} +func (m *QueryBTCDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCDelegationsRequest.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 *QueryBTCDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCDelegationsRequest.Merge(m, src) +} +func (m *QueryBTCDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCDelegationsRequest proto.InternalMessageInfo + +func (m *QueryBTCDelegationsRequest) GetStatus() BTCDelegationStatus { + if m != nil { + return m.Status + } + return BTCDelegationStatus_PENDING +} + +func (m *QueryBTCDelegationsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryBTCDelegationsResponse is the response type for the +// Query/BTCDelegations RPC method. +type QueryBTCDelegationsResponse struct { + // btc_delegations contains all the queried BTC delegations under the given status + BtcDelegations []*BTCDelegation `protobuf:"bytes,1,rep,name=btc_delegations,json=btcDelegations,proto3" json:"btc_delegations,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryBTCDelegationsResponse) Reset() { *m = QueryBTCDelegationsResponse{} } +func (m *QueryBTCDelegationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBTCDelegationsResponse) ProtoMessage() {} +func (*QueryBTCDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{7} +} +func (m *QueryBTCDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCDelegationsResponse.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 *QueryBTCDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCDelegationsResponse.Merge(m, src) +} +func (m *QueryBTCDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCDelegationsResponse proto.InternalMessageInfo + +func (m *QueryBTCDelegationsResponse) GetBtcDelegations() []*BTCDelegation { + if m != nil { + return m.BtcDelegations + } + return nil +} + +func (m *QueryBTCDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryFinalityProviderPowerAtHeightRequest is the request type for the +// Query/FinalityProviderPowerAtHeight RPC method. +type QueryFinalityProviderPowerAtHeightRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider that + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` + // height is used for querying the given finality provider's voting power at this height + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryFinalityProviderPowerAtHeightRequest) Reset() { + *m = QueryFinalityProviderPowerAtHeightRequest{} +} +func (m *QueryFinalityProviderPowerAtHeightRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryFinalityProviderPowerAtHeightRequest) ProtoMessage() {} +func (*QueryFinalityProviderPowerAtHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{8} +} +func (m *QueryFinalityProviderPowerAtHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderPowerAtHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderPowerAtHeightRequest.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 *QueryFinalityProviderPowerAtHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderPowerAtHeightRequest.Merge(m, src) +} +func (m *QueryFinalityProviderPowerAtHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderPowerAtHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderPowerAtHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderPowerAtHeightRequest proto.InternalMessageInfo + +func (m *QueryFinalityProviderPowerAtHeightRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +func (m *QueryFinalityProviderPowerAtHeightRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryFinalityProviderPowerAtHeightResponse is the response type for the +// Query/FinalityProviderPowerAtHeight RPC method. +type QueryFinalityProviderPowerAtHeightResponse struct { + // voting_power is the voting power of the finality provider + VotingPower uint64 `protobuf:"varint,1,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` +} + +func (m *QueryFinalityProviderPowerAtHeightResponse) Reset() { + *m = QueryFinalityProviderPowerAtHeightResponse{} +} +func (m *QueryFinalityProviderPowerAtHeightResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryFinalityProviderPowerAtHeightResponse) ProtoMessage() {} +func (*QueryFinalityProviderPowerAtHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{9} +} +func (m *QueryFinalityProviderPowerAtHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderPowerAtHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderPowerAtHeightResponse.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 *QueryFinalityProviderPowerAtHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderPowerAtHeightResponse.Merge(m, src) +} +func (m *QueryFinalityProviderPowerAtHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderPowerAtHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderPowerAtHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderPowerAtHeightResponse proto.InternalMessageInfo + +func (m *QueryFinalityProviderPowerAtHeightResponse) GetVotingPower() uint64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +// QueryFinalityProviderCurrentPowerRequest is the request type for the +// Query/FinalityProviderCurrentPower RPC method. +type QueryFinalityProviderCurrentPowerRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider that + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` +} + +func (m *QueryFinalityProviderCurrentPowerRequest) Reset() { + *m = QueryFinalityProviderCurrentPowerRequest{} +} +func (m *QueryFinalityProviderCurrentPowerRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProviderCurrentPowerRequest) ProtoMessage() {} +func (*QueryFinalityProviderCurrentPowerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{10} +} +func (m *QueryFinalityProviderCurrentPowerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderCurrentPowerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderCurrentPowerRequest.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 *QueryFinalityProviderCurrentPowerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderCurrentPowerRequest.Merge(m, src) +} +func (m *QueryFinalityProviderCurrentPowerRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderCurrentPowerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderCurrentPowerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderCurrentPowerRequest proto.InternalMessageInfo + +func (m *QueryFinalityProviderCurrentPowerRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +// QueryFinalityProviderCurrentPowerResponse is the response type for the +// Query/FinalityProviderCurrentPower RPC method. +type QueryFinalityProviderCurrentPowerResponse struct { + // height is the current height + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // voting_power is the voting power of the finality provider + VotingPower uint64 `protobuf:"varint,2,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` +} + +func (m *QueryFinalityProviderCurrentPowerResponse) Reset() { + *m = QueryFinalityProviderCurrentPowerResponse{} +} +func (m *QueryFinalityProviderCurrentPowerResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryFinalityProviderCurrentPowerResponse) ProtoMessage() {} +func (*QueryFinalityProviderCurrentPowerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{11} +} +func (m *QueryFinalityProviderCurrentPowerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderCurrentPowerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderCurrentPowerResponse.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 *QueryFinalityProviderCurrentPowerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderCurrentPowerResponse.Merge(m, src) +} +func (m *QueryFinalityProviderCurrentPowerResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderCurrentPowerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderCurrentPowerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderCurrentPowerResponse proto.InternalMessageInfo + +func (m *QueryFinalityProviderCurrentPowerResponse) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *QueryFinalityProviderCurrentPowerResponse) GetVotingPower() uint64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +// QueryActiveFinalityProvidersAtHeightRequest is the request type for the +// Query/ActiveFinalityProvidersAtHeight RPC method. +type QueryActiveFinalityProvidersAtHeightRequest struct { + // height defines at which Babylon height to query the finality providers info. + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryActiveFinalityProvidersAtHeightRequest) Reset() { + *m = QueryActiveFinalityProvidersAtHeightRequest{} +} +func (m *QueryActiveFinalityProvidersAtHeightRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryActiveFinalityProvidersAtHeightRequest) ProtoMessage() {} +func (*QueryActiveFinalityProvidersAtHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{12} +} +func (m *QueryActiveFinalityProvidersAtHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryActiveFinalityProvidersAtHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryActiveFinalityProvidersAtHeightRequest.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 *QueryActiveFinalityProvidersAtHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryActiveFinalityProvidersAtHeightRequest.Merge(m, src) +} +func (m *QueryActiveFinalityProvidersAtHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryActiveFinalityProvidersAtHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryActiveFinalityProvidersAtHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryActiveFinalityProvidersAtHeightRequest proto.InternalMessageInfo + +func (m *QueryActiveFinalityProvidersAtHeightRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *QueryActiveFinalityProvidersAtHeightRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryActiveFinalityProvidersAtHeightResponse is the response type for the +// Query/ActiveFinalityProvidersAtHeight RPC method. +type QueryActiveFinalityProvidersAtHeightResponse struct { + // finality_providers contains all the queried finality providersn. + FinalityProviders []*FinalityProviderWithMeta `protobuf:"bytes,1,rep,name=finality_providers,json=finalityProviders,proto3" json:"finality_providers,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryActiveFinalityProvidersAtHeightResponse) Reset() { + *m = QueryActiveFinalityProvidersAtHeightResponse{} +} +func (m *QueryActiveFinalityProvidersAtHeightResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryActiveFinalityProvidersAtHeightResponse) ProtoMessage() {} +func (*QueryActiveFinalityProvidersAtHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{13} +} +func (m *QueryActiveFinalityProvidersAtHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryActiveFinalityProvidersAtHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryActiveFinalityProvidersAtHeightResponse.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 *QueryActiveFinalityProvidersAtHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryActiveFinalityProvidersAtHeightResponse.Merge(m, src) +} +func (m *QueryActiveFinalityProvidersAtHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryActiveFinalityProvidersAtHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryActiveFinalityProvidersAtHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryActiveFinalityProvidersAtHeightResponse proto.InternalMessageInfo + +func (m *QueryActiveFinalityProvidersAtHeightResponse) GetFinalityProviders() []*FinalityProviderWithMeta { + if m != nil { + return m.FinalityProviders + } + return nil +} + +func (m *QueryActiveFinalityProvidersAtHeightResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryActivatedHeightRequest is the request type for the Query/ActivatedHeight RPC method. +type QueryActivatedHeightRequest struct { +} + +func (m *QueryActivatedHeightRequest) Reset() { *m = QueryActivatedHeightRequest{} } +func (m *QueryActivatedHeightRequest) String() string { return proto.CompactTextString(m) } +func (*QueryActivatedHeightRequest) ProtoMessage() {} +func (*QueryActivatedHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{14} +} +func (m *QueryActivatedHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryActivatedHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryActivatedHeightRequest.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 *QueryActivatedHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryActivatedHeightRequest.Merge(m, src) +} +func (m *QueryActivatedHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryActivatedHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryActivatedHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryActivatedHeightRequest proto.InternalMessageInfo + +// QueryActivatedHeightResponse is the response type for the Query/ActivatedHeight RPC method. +type QueryActivatedHeightResponse struct { + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryActivatedHeightResponse) Reset() { *m = QueryActivatedHeightResponse{} } +func (m *QueryActivatedHeightResponse) String() string { return proto.CompactTextString(m) } +func (*QueryActivatedHeightResponse) ProtoMessage() {} +func (*QueryActivatedHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{15} +} +func (m *QueryActivatedHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryActivatedHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryActivatedHeightResponse.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 *QueryActivatedHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryActivatedHeightResponse.Merge(m, src) +} +func (m *QueryActivatedHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryActivatedHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryActivatedHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryActivatedHeightResponse proto.InternalMessageInfo + +func (m *QueryActivatedHeightResponse) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryFinalityProviderDelegationsRequest is the request type for the +// Query/FinalityProviderDelegations RPC method. +type QueryFinalityProviderDelegationsRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality providerthat + // this BTC delegation delegates to + // the PK follows encoding in BIP-340 spec + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFinalityProviderDelegationsRequest) Reset() { + *m = QueryFinalityProviderDelegationsRequest{} +} +func (m *QueryFinalityProviderDelegationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProviderDelegationsRequest) ProtoMessage() {} +func (*QueryFinalityProviderDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{16} +} +func (m *QueryFinalityProviderDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderDelegationsRequest.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 *QueryFinalityProviderDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderDelegationsRequest.Merge(m, src) +} +func (m *QueryFinalityProviderDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderDelegationsRequest proto.InternalMessageInfo + +func (m *QueryFinalityProviderDelegationsRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +func (m *QueryFinalityProviderDelegationsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryFinalityProviderDelegationsResponse is the response type for the +// Query/FinalityProviderDelegations RPC method. +type QueryFinalityProviderDelegationsResponse struct { + // btc_delegator_delegations contains all the queried BTC delegations. + BtcDelegatorDelegations []*BTCDelegatorDelegations `protobuf:"bytes,1,rep,name=btc_delegator_delegations,json=btcDelegatorDelegations,proto3" json:"btc_delegator_delegations,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFinalityProviderDelegationsResponse) Reset() { + *m = QueryFinalityProviderDelegationsResponse{} +} +func (m *QueryFinalityProviderDelegationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFinalityProviderDelegationsResponse) ProtoMessage() {} +func (*QueryFinalityProviderDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{17} +} +func (m *QueryFinalityProviderDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFinalityProviderDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFinalityProviderDelegationsResponse.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 *QueryFinalityProviderDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFinalityProviderDelegationsResponse.Merge(m, src) +} +func (m *QueryFinalityProviderDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFinalityProviderDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFinalityProviderDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFinalityProviderDelegationsResponse proto.InternalMessageInfo + +func (m *QueryFinalityProviderDelegationsResponse) GetBtcDelegatorDelegations() []*BTCDelegatorDelegations { + if m != nil { + return m.BtcDelegatorDelegations + } + return nil +} + +func (m *QueryFinalityProviderDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryBTCDelegationRequest is the request type to retrieve a BTC delegation by +// staking tx hash +type QueryBTCDelegationRequest struct { + // Hash of staking transaction in btc format + StakingTxHashHex string `protobuf:"bytes,1,opt,name=staking_tx_hash_hex,json=stakingTxHashHex,proto3" json:"staking_tx_hash_hex,omitempty"` +} + +func (m *QueryBTCDelegationRequest) Reset() { *m = QueryBTCDelegationRequest{} } +func (m *QueryBTCDelegationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBTCDelegationRequest) ProtoMessage() {} +func (*QueryBTCDelegationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{18} +} +func (m *QueryBTCDelegationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCDelegationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCDelegationRequest.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 *QueryBTCDelegationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCDelegationRequest.Merge(m, src) +} +func (m *QueryBTCDelegationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCDelegationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCDelegationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCDelegationRequest proto.InternalMessageInfo + +func (m *QueryBTCDelegationRequest) GetStakingTxHashHex() string { + if m != nil { + return m.StakingTxHashHex + } + return "" +} + +// QueryBTCDelegationResponse is response type matching QueryBTCDelegationRequest +// and containing BTC delegation information +type QueryBTCDelegationResponse struct { + // btc_pk is the Bitcoin secp256k1 PK of this BTC delegation + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // fp_btc_pk_list is the list of BIP-340 PKs of the finality providers that + // this BTC delegation delegates to + FpBtcPkList []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,rep,name=fp_btc_pk_list,json=fpBtcPkList,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk_list,omitempty"` + // start_height is the start BTC height of the BTC delegation + // it is the start BTC height of the timelock + StartHeight uint64 `protobuf:"varint,3,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + // end_height is the end height of the BTC delegation + // it is the end BTC height of the timelock - w + EndHeight uint64 `protobuf:"varint,4,opt,name=end_height,json=endHeight,proto3" json:"end_height,omitempty"` + // total_sat is the total amount of BTC stakes in this delegation + // quantified in satoshi + TotalSat uint64 `protobuf:"varint,5,opt,name=total_sat,json=totalSat,proto3" json:"total_sat,omitempty"` + // staking_tx_hex is the hex string of staking tx + StakingTxHex string `protobuf:"bytes,6,opt,name=staking_tx_hex,json=stakingTxHex,proto3" json:"staking_tx_hex,omitempty"` + // covenant_sigs is a list of adaptor signatures on the slashing tx + // by each covenant member + // It will be a part of the witness for the staking tx output. + CovenantSigs []*CovenantAdaptorSignatures `protobuf:"bytes,7,rep,name=covenant_sigs,json=covenantSigs,proto3" json:"covenant_sigs,omitempty"` + // whether this delegation is active + Active bool `protobuf:"varint,8,opt,name=active,proto3" json:"active,omitempty"` + // unbonding_time used in unbonding output timelock path and in slashing transactions + // change outputs + UnbondingTime uint32 `protobuf:"varint,9,opt,name=unbonding_time,json=unbondingTime,proto3" json:"unbonding_time,omitempty"` + // undelegation_info is the undelegation info of this delegation. + UndelegationInfo *BTCUndelegationInfo `protobuf:"bytes,10,opt,name=undelegation_info,json=undelegationInfo,proto3" json:"undelegation_info,omitempty"` +} + +func (m *QueryBTCDelegationResponse) Reset() { *m = QueryBTCDelegationResponse{} } +func (m *QueryBTCDelegationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBTCDelegationResponse) ProtoMessage() {} +func (*QueryBTCDelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74d49d26f7429697, []int{19} +} +func (m *QueryBTCDelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCDelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCDelegationResponse.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 *QueryBTCDelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCDelegationResponse.Merge(m, src) +} +func (m *QueryBTCDelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCDelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCDelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCDelegationResponse proto.InternalMessageInfo + +func (m *QueryBTCDelegationResponse) GetStartHeight() uint64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +func (m *QueryBTCDelegationResponse) GetEndHeight() uint64 { + if m != nil { + return m.EndHeight + } + return 0 +} + +func (m *QueryBTCDelegationResponse) GetTotalSat() uint64 { + if m != nil { + return m.TotalSat + } + return 0 +} + +func (m *QueryBTCDelegationResponse) GetStakingTxHex() string { + if m != nil { + return m.StakingTxHex + } + return "" +} + +func (m *QueryBTCDelegationResponse) GetCovenantSigs() []*CovenantAdaptorSignatures { + if m != nil { + return m.CovenantSigs + } + return nil +} + +func (m *QueryBTCDelegationResponse) GetActive() bool { + if m != nil { + return m.Active + } + return false +} + +func (m *QueryBTCDelegationResponse) GetUnbondingTime() uint32 { + if m != nil { + return m.UnbondingTime + } + return 0 +} + +func (m *QueryBTCDelegationResponse) GetUndelegationInfo() *BTCUndelegationInfo { + if m != nil { + return m.UndelegationInfo + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "babylon.btcstaking.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "babylon.btcstaking.v1.QueryParamsResponse") + proto.RegisterType((*QueryFinalityProvidersRequest)(nil), "babylon.btcstaking.v1.QueryFinalityProvidersRequest") + proto.RegisterType((*QueryFinalityProvidersResponse)(nil), "babylon.btcstaking.v1.QueryFinalityProvidersResponse") + proto.RegisterType((*QueryFinalityProviderRequest)(nil), "babylon.btcstaking.v1.QueryFinalityProviderRequest") + proto.RegisterType((*QueryFinalityProviderResponse)(nil), "babylon.btcstaking.v1.QueryFinalityProviderResponse") + proto.RegisterType((*QueryBTCDelegationsRequest)(nil), "babylon.btcstaking.v1.QueryBTCDelegationsRequest") + proto.RegisterType((*QueryBTCDelegationsResponse)(nil), "babylon.btcstaking.v1.QueryBTCDelegationsResponse") + proto.RegisterType((*QueryFinalityProviderPowerAtHeightRequest)(nil), "babylon.btcstaking.v1.QueryFinalityProviderPowerAtHeightRequest") + proto.RegisterType((*QueryFinalityProviderPowerAtHeightResponse)(nil), "babylon.btcstaking.v1.QueryFinalityProviderPowerAtHeightResponse") + proto.RegisterType((*QueryFinalityProviderCurrentPowerRequest)(nil), "babylon.btcstaking.v1.QueryFinalityProviderCurrentPowerRequest") + proto.RegisterType((*QueryFinalityProviderCurrentPowerResponse)(nil), "babylon.btcstaking.v1.QueryFinalityProviderCurrentPowerResponse") + proto.RegisterType((*QueryActiveFinalityProvidersAtHeightRequest)(nil), "babylon.btcstaking.v1.QueryActiveFinalityProvidersAtHeightRequest") + proto.RegisterType((*QueryActiveFinalityProvidersAtHeightResponse)(nil), "babylon.btcstaking.v1.QueryActiveFinalityProvidersAtHeightResponse") + proto.RegisterType((*QueryActivatedHeightRequest)(nil), "babylon.btcstaking.v1.QueryActivatedHeightRequest") + proto.RegisterType((*QueryActivatedHeightResponse)(nil), "babylon.btcstaking.v1.QueryActivatedHeightResponse") + proto.RegisterType((*QueryFinalityProviderDelegationsRequest)(nil), "babylon.btcstaking.v1.QueryFinalityProviderDelegationsRequest") + proto.RegisterType((*QueryFinalityProviderDelegationsResponse)(nil), "babylon.btcstaking.v1.QueryFinalityProviderDelegationsResponse") + proto.RegisterType((*QueryBTCDelegationRequest)(nil), "babylon.btcstaking.v1.QueryBTCDelegationRequest") + proto.RegisterType((*QueryBTCDelegationResponse)(nil), "babylon.btcstaking.v1.QueryBTCDelegationResponse") +} + +func init() { proto.RegisterFile("babylon/btcstaking/v1/query.proto", fileDescriptor_74d49d26f7429697) } + +var fileDescriptor_74d49d26f7429697 = []byte{ + // 1333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0x45, + 0x1c, 0xce, 0xa4, 0xa9, 0x69, 0x7e, 0x79, 0x34, 0x99, 0x16, 0x70, 0xdd, 0xc6, 0x49, 0x57, 0x6d, + 0xf3, 0x28, 0xec, 0xd6, 0x4e, 0xe8, 0x01, 0x50, 0xdb, 0x38, 0xa5, 0x4d, 0x1f, 0x11, 0x66, 0x93, + 0x52, 0x09, 0x24, 0xac, 0x59, 0x67, 0xbc, 0x5e, 0x9a, 0xec, 0x6c, 0x77, 0xc7, 0xc6, 0x51, 0xd5, + 0x0b, 0x07, 0x6e, 0x48, 0x48, 0xf0, 0x3f, 0x80, 0xc4, 0x11, 0x71, 0x41, 0x5c, 0xb8, 0xf5, 0x58, + 0xc1, 0x01, 0xc4, 0xa1, 0xa0, 0x16, 0x71, 0x40, 0x42, 0xdc, 0x38, 0x23, 0xcf, 0xce, 0xd6, 0x6b, + 0x7b, 0xd7, 0x59, 0x27, 0xe1, 0x56, 0xef, 0xfc, 0x5e, 0xdf, 0x37, 0xdf, 0x7e, 0xfb, 0x6b, 0xe0, + 0xb4, 0x41, 0x8c, 0x9d, 0x2d, 0x66, 0x6b, 0x06, 0x2f, 0x7b, 0x9c, 0xdc, 0xb3, 0x6c, 0x53, 0xab, + 0xe7, 0xb4, 0xfb, 0x35, 0xea, 0xee, 0xa8, 0x8e, 0xcb, 0x38, 0xc3, 0x2f, 0xca, 0x10, 0xb5, 0x15, + 0xa2, 0xd6, 0x73, 0x99, 0xe3, 0x26, 0x33, 0x99, 0x88, 0xd0, 0x9a, 0xff, 0xf2, 0x83, 0x33, 0xa7, + 0x4c, 0xc6, 0xcc, 0x2d, 0xaa, 0x11, 0xc7, 0xd2, 0x88, 0x6d, 0x33, 0x4e, 0xb8, 0xc5, 0x6c, 0x4f, + 0x9e, 0x2e, 0x94, 0x99, 0xb7, 0xcd, 0x3c, 0xcd, 0x20, 0x1e, 0xf5, 0x7b, 0x68, 0xf5, 0x9c, 0x41, + 0x39, 0xc9, 0x69, 0x0e, 0x31, 0x2d, 0x5b, 0x04, 0xcb, 0x58, 0x25, 0x7a, 0x32, 0x87, 0xb8, 0x64, + 0x3b, 0xa8, 0x77, 0x2e, 0x3a, 0x26, 0x34, 0xa8, 0x88, 0x53, 0x8e, 0x03, 0x7e, 0xa7, 0xd9, 0xad, + 0x28, 0x92, 0x75, 0x7a, 0xbf, 0x46, 0x3d, 0xae, 0xe8, 0x70, 0xac, 0xed, 0xa9, 0xe7, 0x30, 0xdb, + 0xa3, 0xf8, 0x0d, 0x48, 0xf9, 0x4d, 0xd2, 0x68, 0x06, 0xcd, 0x8d, 0xe4, 0xa7, 0xd4, 0x48, 0x02, + 0x54, 0x3f, 0xad, 0x30, 0xf4, 0xe8, 0xc9, 0xf4, 0x80, 0x2e, 0x53, 0x14, 0x13, 0xa6, 0x44, 0xcd, + 0x6b, 0x96, 0x4d, 0xb6, 0x2c, 0xbe, 0x53, 0x74, 0x59, 0xdd, 0xda, 0xa4, 0x6e, 0xd0, 0x14, 0x5f, + 0x03, 0x68, 0x41, 0x95, 0x1d, 0xce, 0xa9, 0x3e, 0x2f, 0x6a, 0x93, 0x17, 0xd5, 0xe7, 0x5e, 0xf2, + 0xa2, 0x16, 0x89, 0x49, 0x65, 0xae, 0x1e, 0xca, 0x54, 0x7e, 0x40, 0x90, 0x8d, 0xeb, 0x24, 0x81, + 0xbc, 0x0b, 0xb8, 0x22, 0x0f, 0x4b, 0x4e, 0x70, 0x9a, 0x46, 0x33, 0x87, 0xe6, 0x46, 0xf2, 0xb3, + 0x31, 0xa0, 0x3a, 0xab, 0xe9, 0x93, 0x95, 0xce, 0xfa, 0xf8, 0x7a, 0x1b, 0x84, 0x41, 0x01, 0x61, + 0x76, 0x57, 0x08, 0xfe, 0x50, 0x6d, 0x18, 0x96, 0xe1, 0x54, 0x24, 0x84, 0x80, 0xab, 0xd3, 0x30, + 0x56, 0x71, 0x4a, 0x06, 0x2f, 0x97, 0x9c, 0x7b, 0xa5, 0x2a, 0x6d, 0x08, 0xba, 0x86, 0x75, 0xa8, + 0x38, 0x05, 0x5e, 0x2e, 0xde, 0x5b, 0xa5, 0x0d, 0xa5, 0x16, 0xc3, 0xf7, 0x73, 0x12, 0x36, 0x60, + 0xb2, 0x8b, 0x04, 0x49, 0x7b, 0x62, 0x0e, 0x26, 0x3a, 0x39, 0x50, 0xbe, 0x42, 0x90, 0x11, 0x7d, + 0x0b, 0x1b, 0x2b, 0x57, 0xe9, 0x16, 0x35, 0x7d, 0x99, 0x07, 0x83, 0x17, 0x20, 0xe5, 0x71, 0xc2, + 0x6b, 0xbe, 0x84, 0xc6, 0xf3, 0x0b, 0x31, 0x9d, 0xda, 0xb2, 0xd7, 0x45, 0x86, 0x2e, 0x33, 0x3b, + 0x84, 0x32, 0xb8, 0x67, 0xa1, 0x7c, 0x8b, 0xe0, 0x64, 0xe4, 0xa8, 0x92, 0xa0, 0x35, 0x38, 0xda, + 0x64, 0x78, 0xb3, 0x75, 0x24, 0x25, 0x72, 0x26, 0xc9, 0xd0, 0xfa, 0xb8, 0xc1, 0xcb, 0xa1, 0xb2, + 0x07, 0x27, 0x8e, 0x0a, 0xcc, 0x47, 0xde, 0x6c, 0x91, 0x7d, 0x44, 0xdd, 0x65, 0xbe, 0x4a, 0x2d, + 0xb3, 0xca, 0x93, 0x2b, 0x05, 0xbf, 0x04, 0xa9, 0xaa, 0xc8, 0x11, 0x43, 0x0d, 0xe9, 0xf2, 0x97, + 0xf2, 0x36, 0x2c, 0x24, 0xe9, 0x23, 0xd9, 0x3a, 0x0d, 0xa3, 0x75, 0xc6, 0x2d, 0xdb, 0x2c, 0x39, + 0xcd, 0x73, 0xd1, 0x67, 0x48, 0x1f, 0xf1, 0x9f, 0x89, 0x14, 0x65, 0x0d, 0xe6, 0x22, 0x0b, 0xae, + 0xd4, 0x5c, 0x97, 0xda, 0x5c, 0x04, 0xf5, 0xa1, 0xf0, 0x38, 0x1e, 0xda, 0xcb, 0xc9, 0xf1, 0x5a, + 0x20, 0x51, 0x18, 0x64, 0xd7, 0xd8, 0x83, 0xdd, 0x63, 0x7f, 0x8a, 0xe0, 0xbc, 0x68, 0xb4, 0x5c, + 0xe6, 0x56, 0x9d, 0x76, 0xd9, 0x4a, 0x27, 0xe5, 0x71, 0xad, 0x0e, 0x4a, 0xb7, 0x3f, 0x23, 0x78, + 0x25, 0xd9, 0x3c, 0x12, 0xfb, 0x07, 0x3d, 0xec, 0x4e, 0x4b, 0xf8, 0xaa, 0xdf, 0xb5, 0x78, 0x75, + 0x8d, 0x72, 0xf2, 0xbf, 0xda, 0xde, 0x94, 0x7c, 0x21, 0x05, 0x30, 0xc2, 0xe9, 0x66, 0x1b, 0xb1, + 0xca, 0x45, 0xe9, 0x8a, 0x5d, 0xc7, 0xbd, 0xef, 0x58, 0xf9, 0x02, 0xc1, 0x6c, 0xa4, 0x52, 0x22, + 0x0c, 0x2a, 0xc1, 0xfb, 0x72, 0x50, 0xf7, 0xf8, 0x1b, 0x8a, 0x79, 0x1f, 0xa2, 0xcc, 0xe8, 0x43, + 0x38, 0x11, 0x32, 0x23, 0xe6, 0x46, 0xd8, 0x92, 0xba, 0xab, 0x2d, 0xb1, 0xb6, 0xd2, 0x2f, 0xb7, + 0x0c, 0xaa, 0xed, 0xe0, 0xe0, 0xee, 0xf3, 0x26, 0x9c, 0xe8, 0x36, 0xd8, 0x80, 0xe9, 0x57, 0xe1, + 0x98, 0x1c, 0xb2, 0xc4, 0x1b, 0xa5, 0x2a, 0xf1, 0xaa, 0x21, 0xbe, 0x27, 0xe4, 0xd1, 0x46, 0x63, + 0x95, 0x78, 0xd5, 0xe6, 0xdb, 0xfe, 0xfd, 0x50, 0xd4, 0x87, 0x25, 0x64, 0xd6, 0x29, 0xff, 0xd2, + 0x44, 0x81, 0xd1, 0xc2, 0xc5, 0x5f, 0x9f, 0x4c, 0xe7, 0x4d, 0x8b, 0x57, 0x6b, 0x86, 0x5a, 0x66, + 0xdb, 0x9a, 0xa4, 0xa6, 0x5c, 0x25, 0x96, 0x1d, 0xfc, 0xd0, 0xf8, 0x8e, 0x43, 0x3d, 0xb5, 0x70, + 0xa3, 0xb8, 0xb8, 0x74, 0xa1, 0x58, 0x33, 0x6e, 0xd1, 0x1d, 0xfd, 0xb0, 0xd1, 0xbc, 0x66, 0xfc, + 0x3e, 0x8c, 0xb7, 0x64, 0xb0, 0x65, 0x79, 0x4d, 0x6f, 0x3c, 0xb4, 0x8f, 0xb2, 0x23, 0x52, 0x3f, + 0xb7, 0x2d, 0xa1, 0xb1, 0x51, 0x8f, 0x13, 0x97, 0x97, 0xa4, 0x5a, 0x0f, 0xf9, 0x9e, 0x23, 0x9e, + 0xf9, 0x92, 0xc6, 0x53, 0x00, 0xd4, 0xde, 0x0c, 0x02, 0x86, 0x44, 0xc0, 0x30, 0xb5, 0xa5, 0xe2, + 0xf1, 0x49, 0x18, 0xe6, 0x8c, 0x93, 0xad, 0x92, 0x47, 0x78, 0xfa, 0xb0, 0x38, 0x3d, 0x22, 0x1e, + 0xac, 0x13, 0x8e, 0xcf, 0xc0, 0x78, 0x98, 0x58, 0xda, 0x48, 0xa7, 0x04, 0xa7, 0xa3, 0x2d, 0x4e, + 0x69, 0x03, 0xdf, 0x81, 0xb1, 0x32, 0xab, 0x53, 0x9b, 0xd8, 0xbc, 0xe4, 0x59, 0xa6, 0x97, 0x7e, + 0x41, 0x88, 0xe8, 0x42, 0x8c, 0x88, 0x56, 0x64, 0xec, 0xf2, 0x26, 0x71, 0x38, 0x73, 0xd7, 0x2d, + 0xd3, 0x26, 0xbc, 0xe6, 0x52, 0x4f, 0x1f, 0x0d, 0xca, 0xac, 0x5b, 0xa6, 0xd7, 0x7c, 0x07, 0x89, + 0xb0, 0xa5, 0xf4, 0x91, 0x19, 0x34, 0x77, 0x44, 0x97, 0xbf, 0xf0, 0x59, 0x18, 0xaf, 0xd9, 0x06, + 0xb3, 0x37, 0xc5, 0x58, 0xd6, 0x36, 0x4d, 0x0f, 0xcf, 0xa0, 0xb9, 0x31, 0x7d, 0xec, 0xf9, 0xd3, + 0x0d, 0x6b, 0x9b, 0xe2, 0xbb, 0x30, 0x59, 0xb3, 0x5b, 0xd2, 0x2e, 0x59, 0x76, 0x85, 0xa5, 0x41, + 0x28, 0xb0, 0xc7, 0xaa, 0x70, 0x27, 0x94, 0x72, 0xc3, 0xae, 0x30, 0x7d, 0xa2, 0xd6, 0xf1, 0x24, + 0xff, 0xcf, 0x51, 0x38, 0x2c, 0xe4, 0x83, 0x3f, 0x41, 0x90, 0xf2, 0x37, 0x54, 0x3c, 0x1f, 0x53, + 0xb2, 0x7b, 0x25, 0xce, 0x2c, 0x24, 0x09, 0xf5, 0xb5, 0xa8, 0x9c, 0xfd, 0xf8, 0xa7, 0x3f, 0x3e, + 0x1f, 0x9c, 0xc6, 0x53, 0x5a, 0xaf, 0x4d, 0x1d, 0x7f, 0x83, 0x60, 0xb2, 0xcb, 0xbc, 0xf1, 0x52, + 0xaf, 0x46, 0x71, 0xcb, 0x73, 0xe6, 0xb5, 0x3e, 0xb3, 0xe4, 0xa4, 0x39, 0x31, 0xe9, 0x79, 0x3c, + 0x1f, 0x33, 0x69, 0xf7, 0x67, 0x03, 0xff, 0x88, 0x60, 0xa2, 0xb3, 0x20, 0x5e, 0xec, 0xa7, 0x7d, + 0x30, 0xf3, 0x52, 0x7f, 0x49, 0x72, 0xe4, 0x75, 0x31, 0xf2, 0x1a, 0xbe, 0x95, 0x78, 0x64, 0xed, + 0x41, 0x9b, 0xa3, 0x3f, 0xec, 0x0e, 0xc1, 0x5f, 0x22, 0x18, 0x6f, 0xdf, 0x02, 0x71, 0xae, 0xd7, + 0x74, 0x91, 0xcb, 0x6d, 0x26, 0xdf, 0x4f, 0x8a, 0x84, 0xa3, 0x0a, 0x38, 0x73, 0xf8, 0x9c, 0x16, + 0xfb, 0x3f, 0xb6, 0xb0, 0xd5, 0xe3, 0x3f, 0x11, 0x4c, 0xef, 0xf2, 0xdd, 0xc7, 0x85, 0x5e, 0x73, + 0x24, 0x5b, 0x62, 0x32, 0x2b, 0xfb, 0xaa, 0x21, 0xc1, 0xbd, 0x2e, 0xc0, 0x2d, 0xe1, 0x7c, 0x1f, + 0x77, 0xe5, 0x5b, 0xde, 0x43, 0xfc, 0x2f, 0x82, 0xa9, 0x9e, 0x9b, 0x27, 0xbe, 0xd2, 0x8f, 0x7e, + 0xa2, 0x96, 0xe3, 0xcc, 0xf2, 0x3e, 0x2a, 0x48, 0x88, 0x45, 0x01, 0xf1, 0x26, 0x5e, 0xdd, 0xbb, + 0x1c, 0xc5, 0xe2, 0xd9, 0x02, 0xfe, 0x17, 0x82, 0x53, 0xbd, 0x56, 0x5a, 0x7c, 0xb9, 0x9f, 0xa9, + 0x23, 0x76, 0xeb, 0xcc, 0x95, 0xbd, 0x17, 0x90, 0xa8, 0xaf, 0x0b, 0xd4, 0xcb, 0xf8, 0xf2, 0x3e, + 0x51, 0xe3, 0xaf, 0x11, 0x1c, 0xed, 0x58, 0xe7, 0x70, 0x7e, 0x57, 0xe9, 0x75, 0xad, 0x86, 0x99, + 0xc5, 0xbe, 0x72, 0x24, 0x0a, 0x4d, 0xa0, 0x98, 0xc7, 0xb3, 0x31, 0x28, 0x48, 0x90, 0x27, 0xbf, + 0xc3, 0xf8, 0x6f, 0x04, 0x27, 0x7b, 0x2c, 0x6b, 0xf8, 0x52, 0x3f, 0xc4, 0x46, 0x18, 0xc8, 0xe5, + 0x3d, 0xe7, 0x4b, 0x44, 0x6b, 0x02, 0xd1, 0x75, 0xfc, 0xd6, 0xde, 0xef, 0x25, 0x6c, 0x36, 0xdf, + 0x21, 0x18, 0x6b, 0xf3, 0x2d, 0x7c, 0x21, 0xb1, 0xc5, 0x05, 0x98, 0x72, 0x7d, 0x64, 0x48, 0x14, + 0x57, 0x05, 0x8a, 0x4b, 0xf8, 0xcd, 0x64, 0x9e, 0xa8, 0x3d, 0x88, 0xd8, 0x23, 0x1f, 0x16, 0x6e, + 0x3f, 0x7a, 0x9a, 0x45, 0x8f, 0x9f, 0x66, 0xd1, 0xef, 0x4f, 0xb3, 0xe8, 0xb3, 0x67, 0xd9, 0x81, + 0xc7, 0xcf, 0xb2, 0x03, 0xbf, 0x3c, 0xcb, 0x0e, 0xbc, 0xb7, 0xeb, 0x02, 0xd7, 0x08, 0x37, 0x14, + 0xdb, 0x9c, 0x91, 0x12, 0x7f, 0x2f, 0x5b, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x11, 0x52, + 0x4c, 0x17, 0x14, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// 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 { + // Parameters queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // FinalityProviders queries all finality providers + FinalityProviders(ctx context.Context, in *QueryFinalityProvidersRequest, opts ...grpc.CallOption) (*QueryFinalityProvidersResponse, error) + // FinalityProvider info about one finality provider + FinalityProvider(ctx context.Context, in *QueryFinalityProviderRequest, opts ...grpc.CallOption) (*QueryFinalityProviderResponse, error) + // BTCDelegations queries all BTC delegations under a given status + BTCDelegations(ctx context.Context, in *QueryBTCDelegationsRequest, opts ...grpc.CallOption) (*QueryBTCDelegationsResponse, error) + // ActiveFinalityProvidersAtHeight queries finality providers with non zero voting power at given height. + ActiveFinalityProvidersAtHeight(ctx context.Context, in *QueryActiveFinalityProvidersAtHeightRequest, opts ...grpc.CallOption) (*QueryActiveFinalityProvidersAtHeightResponse, error) + // FinalityProviderPowerAtHeight queries the voting power of a finality provider at a given height + FinalityProviderPowerAtHeight(ctx context.Context, in *QueryFinalityProviderPowerAtHeightRequest, opts ...grpc.CallOption) (*QueryFinalityProviderPowerAtHeightResponse, error) + // FinalityProviderCurrentPower queries the voting power of a finality provider at the current height + FinalityProviderCurrentPower(ctx context.Context, in *QueryFinalityProviderCurrentPowerRequest, opts ...grpc.CallOption) (*QueryFinalityProviderCurrentPowerResponse, error) + // ActivatedHeight queries the height when BTC staking protocol is activated, i.e., the first height when + // there exists 1 finality provider with voting power + ActivatedHeight(ctx context.Context, in *QueryActivatedHeightRequest, opts ...grpc.CallOption) (*QueryActivatedHeightResponse, error) + // FinalityProviderDelegations queries all BTC delegations of the given finality provider + FinalityProviderDelegations(ctx context.Context, in *QueryFinalityProviderDelegationsRequest, opts ...grpc.CallOption) (*QueryFinalityProviderDelegationsResponse, error) + // BTCDelegation retrieves delegation by corresponding staking tx hash + BTCDelegation(ctx context.Context, in *QueryBTCDelegationRequest, opts ...grpc.CallOption) (*QueryBTCDelegationResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +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.btcstaking.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FinalityProviders(ctx context.Context, in *QueryFinalityProvidersRequest, opts ...grpc.CallOption) (*QueryFinalityProvidersResponse, error) { + out := new(QueryFinalityProvidersResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/FinalityProviders", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FinalityProvider(ctx context.Context, in *QueryFinalityProviderRequest, opts ...grpc.CallOption) (*QueryFinalityProviderResponse, error) { + out := new(QueryFinalityProviderResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/FinalityProvider", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BTCDelegations(ctx context.Context, in *QueryBTCDelegationsRequest, opts ...grpc.CallOption) (*QueryBTCDelegationsResponse, error) { + out := new(QueryBTCDelegationsResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/BTCDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ActiveFinalityProvidersAtHeight(ctx context.Context, in *QueryActiveFinalityProvidersAtHeightRequest, opts ...grpc.CallOption) (*QueryActiveFinalityProvidersAtHeightResponse, error) { + out := new(QueryActiveFinalityProvidersAtHeightResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/ActiveFinalityProvidersAtHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FinalityProviderPowerAtHeight(ctx context.Context, in *QueryFinalityProviderPowerAtHeightRequest, opts ...grpc.CallOption) (*QueryFinalityProviderPowerAtHeightResponse, error) { + out := new(QueryFinalityProviderPowerAtHeightResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/FinalityProviderPowerAtHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FinalityProviderCurrentPower(ctx context.Context, in *QueryFinalityProviderCurrentPowerRequest, opts ...grpc.CallOption) (*QueryFinalityProviderCurrentPowerResponse, error) { + out := new(QueryFinalityProviderCurrentPowerResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/FinalityProviderCurrentPower", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ActivatedHeight(ctx context.Context, in *QueryActivatedHeightRequest, opts ...grpc.CallOption) (*QueryActivatedHeightResponse, error) { + out := new(QueryActivatedHeightResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/ActivatedHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FinalityProviderDelegations(ctx context.Context, in *QueryFinalityProviderDelegationsRequest, opts ...grpc.CallOption) (*QueryFinalityProviderDelegationsResponse, error) { + out := new(QueryFinalityProviderDelegationsResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/FinalityProviderDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BTCDelegation(ctx context.Context, in *QueryBTCDelegationRequest, opts ...grpc.CallOption) (*QueryBTCDelegationResponse, error) { + out := new(QueryBTCDelegationResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Query/BTCDelegation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Parameters queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // FinalityProviders queries all finality providers + FinalityProviders(context.Context, *QueryFinalityProvidersRequest) (*QueryFinalityProvidersResponse, error) + // FinalityProvider info about one finality provider + FinalityProvider(context.Context, *QueryFinalityProviderRequest) (*QueryFinalityProviderResponse, error) + // BTCDelegations queries all BTC delegations under a given status + BTCDelegations(context.Context, *QueryBTCDelegationsRequest) (*QueryBTCDelegationsResponse, error) + // ActiveFinalityProvidersAtHeight queries finality providers with non zero voting power at given height. + ActiveFinalityProvidersAtHeight(context.Context, *QueryActiveFinalityProvidersAtHeightRequest) (*QueryActiveFinalityProvidersAtHeightResponse, error) + // FinalityProviderPowerAtHeight queries the voting power of a finality provider at a given height + FinalityProviderPowerAtHeight(context.Context, *QueryFinalityProviderPowerAtHeightRequest) (*QueryFinalityProviderPowerAtHeightResponse, error) + // FinalityProviderCurrentPower queries the voting power of a finality provider at the current height + FinalityProviderCurrentPower(context.Context, *QueryFinalityProviderCurrentPowerRequest) (*QueryFinalityProviderCurrentPowerResponse, error) + // ActivatedHeight queries the height when BTC staking protocol is activated, i.e., the first height when + // there exists 1 finality provider with voting power + ActivatedHeight(context.Context, *QueryActivatedHeightRequest) (*QueryActivatedHeightResponse, error) + // FinalityProviderDelegations queries all BTC delegations of the given finality provider + FinalityProviderDelegations(context.Context, *QueryFinalityProviderDelegationsRequest) (*QueryFinalityProviderDelegationsResponse, error) + // BTCDelegation retrieves delegation by corresponding staking tx hash + BTCDelegation(context.Context, *QueryBTCDelegationRequest) (*QueryBTCDelegationResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +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) FinalityProviders(ctx context.Context, req *QueryFinalityProvidersRequest) (*QueryFinalityProvidersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalityProviders not implemented") +} +func (*UnimplementedQueryServer) FinalityProvider(ctx context.Context, req *QueryFinalityProviderRequest) (*QueryFinalityProviderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalityProvider not implemented") +} +func (*UnimplementedQueryServer) BTCDelegations(ctx context.Context, req *QueryBTCDelegationsRequest) (*QueryBTCDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BTCDelegations not implemented") +} +func (*UnimplementedQueryServer) ActiveFinalityProvidersAtHeight(ctx context.Context, req *QueryActiveFinalityProvidersAtHeightRequest) (*QueryActiveFinalityProvidersAtHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ActiveFinalityProvidersAtHeight not implemented") +} +func (*UnimplementedQueryServer) FinalityProviderPowerAtHeight(ctx context.Context, req *QueryFinalityProviderPowerAtHeightRequest) (*QueryFinalityProviderPowerAtHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalityProviderPowerAtHeight not implemented") +} +func (*UnimplementedQueryServer) FinalityProviderCurrentPower(ctx context.Context, req *QueryFinalityProviderCurrentPowerRequest) (*QueryFinalityProviderCurrentPowerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalityProviderCurrentPower not implemented") +} +func (*UnimplementedQueryServer) ActivatedHeight(ctx context.Context, req *QueryActivatedHeightRequest) (*QueryActivatedHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ActivatedHeight not implemented") +} +func (*UnimplementedQueryServer) FinalityProviderDelegations(ctx context.Context, req *QueryFinalityProviderDelegationsRequest) (*QueryFinalityProviderDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalityProviderDelegations not implemented") +} +func (*UnimplementedQueryServer) BTCDelegation(ctx context.Context, req *QueryBTCDelegationRequest) (*QueryBTCDelegationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BTCDelegation not implemented") +} + +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.btcstaking.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_FinalityProviders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFinalityProvidersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FinalityProviders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/FinalityProviders", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FinalityProviders(ctx, req.(*QueryFinalityProvidersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FinalityProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFinalityProviderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FinalityProvider(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/FinalityProvider", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FinalityProvider(ctx, req.(*QueryFinalityProviderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BTCDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBTCDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BTCDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/BTCDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BTCDelegations(ctx, req.(*QueryBTCDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ActiveFinalityProvidersAtHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryActiveFinalityProvidersAtHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ActiveFinalityProvidersAtHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/ActiveFinalityProvidersAtHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ActiveFinalityProvidersAtHeight(ctx, req.(*QueryActiveFinalityProvidersAtHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FinalityProviderPowerAtHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFinalityProviderPowerAtHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FinalityProviderPowerAtHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/FinalityProviderPowerAtHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FinalityProviderPowerAtHeight(ctx, req.(*QueryFinalityProviderPowerAtHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FinalityProviderCurrentPower_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFinalityProviderCurrentPowerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FinalityProviderCurrentPower(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/FinalityProviderCurrentPower", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FinalityProviderCurrentPower(ctx, req.(*QueryFinalityProviderCurrentPowerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ActivatedHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryActivatedHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ActivatedHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/ActivatedHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ActivatedHeight(ctx, req.(*QueryActivatedHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FinalityProviderDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFinalityProviderDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FinalityProviderDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/FinalityProviderDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FinalityProviderDelegations(ctx, req.(*QueryFinalityProviderDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BTCDelegation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBTCDelegationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BTCDelegation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Query/BTCDelegation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BTCDelegation(ctx, req.(*QueryBTCDelegationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "babylon.btcstaking.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "FinalityProviders", + Handler: _Query_FinalityProviders_Handler, + }, + { + MethodName: "FinalityProvider", + Handler: _Query_FinalityProvider_Handler, + }, + { + MethodName: "BTCDelegations", + Handler: _Query_BTCDelegations_Handler, + }, + { + MethodName: "ActiveFinalityProvidersAtHeight", + Handler: _Query_ActiveFinalityProvidersAtHeight_Handler, + }, + { + MethodName: "FinalityProviderPowerAtHeight", + Handler: _Query_FinalityProviderPowerAtHeight_Handler, + }, + { + MethodName: "FinalityProviderCurrentPower", + Handler: _Query_FinalityProviderCurrentPower_Handler, + }, + { + MethodName: "ActivatedHeight", + Handler: _Query_ActivatedHeight_Handler, + }, + { + MethodName: "FinalityProviderDelegations", + Handler: _Query_FinalityProviderDelegations_Handler, + }, + { + MethodName: "BTCDelegation", + Handler: _Query_BTCDelegation_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/btcstaking/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 *QueryFinalityProvidersRequest) 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 *QueryFinalityProvidersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProvidersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.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 *QueryFinalityProvidersResponse) 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 *QueryFinalityProvidersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProvidersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.FinalityProviders) > 0 { + for iNdEx := len(m.FinalityProviders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FinalityProviders[iNdEx].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 *QueryFinalityProviderRequest) 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 *QueryFinalityProviderRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderResponse) 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 *QueryFinalityProviderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FinalityProvider != nil { + { + size, err := m.FinalityProvider.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 *QueryBTCDelegationsRequest) 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 *QueryBTCDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCDelegationsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Status != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryBTCDelegationsResponse) 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 *QueryBTCDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCDelegationsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.BtcDelegations) > 0 { + for iNdEx := len(m.BtcDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BtcDelegations[iNdEx].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 *QueryFinalityProviderPowerAtHeightRequest) 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 *QueryFinalityProviderPowerAtHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderPowerAtHeightRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderPowerAtHeightResponse) 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 *QueryFinalityProviderPowerAtHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderPowerAtHeightResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.VotingPower != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderCurrentPowerRequest) 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 *QueryFinalityProviderCurrentPowerRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderCurrentPowerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderCurrentPowerResponse) 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 *QueryFinalityProviderCurrentPowerResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderCurrentPowerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.VotingPower != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryActiveFinalityProvidersAtHeightRequest) 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 *QueryActiveFinalityProvidersAtHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryActiveFinalityProvidersAtHeightRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryActiveFinalityProvidersAtHeightResponse) 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 *QueryActiveFinalityProvidersAtHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryActiveFinalityProvidersAtHeightResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.FinalityProviders) > 0 { + for iNdEx := len(m.FinalityProviders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FinalityProviders[iNdEx].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 *QueryActivatedHeightRequest) 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 *QueryActivatedHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryActivatedHeightRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryActivatedHeightResponse) 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 *QueryActivatedHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryActivatedHeightResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderDelegationsRequest) 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 *QueryFinalityProviderDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderDelegationsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFinalityProviderDelegationsResponse) 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 *QueryFinalityProviderDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFinalityProviderDelegationsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.BtcDelegatorDelegations) > 0 { + for iNdEx := len(m.BtcDelegatorDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BtcDelegatorDelegations[iNdEx].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 *QueryBTCDelegationRequest) 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 *QueryBTCDelegationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCDelegationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StakingTxHashHex) > 0 { + i -= len(m.StakingTxHashHex) + copy(dAtA[i:], m.StakingTxHashHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.StakingTxHashHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBTCDelegationResponse) 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 *QueryBTCDelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCDelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UndelegationInfo != nil { + { + size, err := m.UndelegationInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + if m.UnbondingTime != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.UnbondingTime)) + i-- + dAtA[i] = 0x48 + } + if m.Active { + i-- + if m.Active { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.CovenantSigs) > 0 { + for iNdEx := len(m.CovenantSigs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CovenantSigs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.StakingTxHex) > 0 { + i -= len(m.StakingTxHex) + copy(dAtA[i:], m.StakingTxHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.StakingTxHex))) + i-- + dAtA[i] = 0x32 + } + if m.TotalSat != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.TotalSat)) + i-- + dAtA[i] = 0x28 + } + if m.EndHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.EndHeight)) + i-- + dAtA[i] = 0x20 + } + if m.StartHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.FpBtcPkList) > 0 { + for iNdEx := len(m.FpBtcPkList) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.FpBtcPkList[iNdEx].Size() + i -= size + if _, err := m.FpBtcPkList[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + 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 *QueryFinalityProvidersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProvidersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FinalityProviders) > 0 { + for _, e := range m.FinalityProviders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProviderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProviderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FinalityProvider != nil { + l = m.FinalityProvider.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBTCDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Status != 0 { + n += 1 + sovQuery(uint64(m.Status)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBTCDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BtcDelegations) > 0 { + for _, e := range m.BtcDelegations { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProviderPowerAtHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryFinalityProviderPowerAtHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VotingPower != 0 { + n += 1 + sovQuery(uint64(m.VotingPower)) + } + return n +} + +func (m *QueryFinalityProviderCurrentPowerRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProviderCurrentPowerResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + if m.VotingPower != 0 { + n += 1 + sovQuery(uint64(m.VotingPower)) + } + return n +} + +func (m *QueryActiveFinalityProvidersAtHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryActiveFinalityProvidersAtHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FinalityProviders) > 0 { + for _, e := range m.FinalityProviders { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryActivatedHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryActivatedHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryFinalityProviderDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFinalityProviderDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BtcDelegatorDelegations) > 0 { + for _, e := range m.BtcDelegatorDelegations { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBTCDelegationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.StakingTxHashHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBTCDelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.FpBtcPkList) > 0 { + for _, e := range m.FpBtcPkList { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.StartHeight != 0 { + n += 1 + sovQuery(uint64(m.StartHeight)) + } + if m.EndHeight != 0 { + n += 1 + sovQuery(uint64(m.EndHeight)) + } + if m.TotalSat != 0 { + n += 1 + sovQuery(uint64(m.TotalSat)) + } + l = len(m.StakingTxHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.CovenantSigs) > 0 { + for _, e := range m.CovenantSigs { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Active { + n += 2 + } + if m.UnbondingTime != 0 { + n += 1 + sovQuery(uint64(m.UnbondingTime)) + } + if m.UndelegationInfo != nil { + l = m.UndelegationInfo.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +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 *QueryFinalityProvidersRequest) 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: QueryFinalityProvidersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProvidersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryFinalityProvidersResponse) 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: QueryFinalityProvidersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProvidersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalityProviders", 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 + } + m.FinalityProviders = append(m.FinalityProviders, &FinalityProvider{}) + if err := m.FinalityProviders[len(m.FinalityProviders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryFinalityProviderRequest) 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: QueryFinalityProviderRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + 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 *QueryFinalityProviderResponse) 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: QueryFinalityProviderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalityProvider", 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 m.FinalityProvider == nil { + m.FinalityProvider = &FinalityProvider{} + } + if err := m.FinalityProvider.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 *QueryBTCDelegationsRequest) 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: QueryBTCDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= BTCDelegationStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryBTCDelegationsResponse) 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: QueryBTCDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcDelegations", 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 + } + m.BtcDelegations = append(m.BtcDelegations, &BTCDelegation{}) + if err := m.BtcDelegations[len(m.BtcDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryFinalityProviderPowerAtHeightRequest) 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: QueryFinalityProviderPowerAtHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderPowerAtHeightRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryFinalityProviderPowerAtHeightResponse) 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: QueryFinalityProviderPowerAtHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderPowerAtHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryFinalityProviderCurrentPowerRequest) 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: QueryFinalityProviderCurrentPowerRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderCurrentPowerRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + 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 *QueryFinalityProviderCurrentPowerResponse) 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: QueryFinalityProviderCurrentPowerResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderCurrentPowerResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryActiveFinalityProvidersAtHeightRequest) 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: QueryActiveFinalityProvidersAtHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryActiveFinalityProvidersAtHeightRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryActiveFinalityProvidersAtHeightResponse) 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: QueryActiveFinalityProvidersAtHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryActiveFinalityProvidersAtHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalityProviders", 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 + } + m.FinalityProviders = append(m.FinalityProviders, &FinalityProviderWithMeta{}) + if err := m.FinalityProviders[len(m.FinalityProviders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryActivatedHeightRequest) 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: QueryActivatedHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryActivatedHeightRequest: 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 *QueryActivatedHeightResponse) 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: QueryActivatedHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryActivatedHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryFinalityProviderDelegationsRequest) 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: QueryFinalityProviderDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryFinalityProviderDelegationsResponse) 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: QueryFinalityProviderDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFinalityProviderDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcDelegatorDelegations", 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 + } + m.BtcDelegatorDelegations = append(m.BtcDelegatorDelegations, &BTCDelegatorDelegations{}) + if err := m.BtcDelegatorDelegations[len(m.BtcDelegatorDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryBTCDelegationRequest) 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: QueryBTCDelegationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCDelegationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHashHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTxHashHex = string(dAtA[iNdEx:postIndex]) + 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 *QueryBTCDelegationResponse) 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: QueryBTCDelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCDelegationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPkList = append(m.FpBtcPkList, v) + if err := m.FpBtcPkList[len(m.FpBtcPkList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EndHeight", wireType) + } + m.EndHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EndHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSat", wireType) + } + m.TotalSat = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalSat |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StakingTxHex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CovenantSigs", 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 + } + m.CovenantSigs = append(m.CovenantSigs, &CovenantAdaptorSignatures{}) + if err := m.CovenantSigs[len(m.CovenantSigs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Active", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Active = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + m.UnbondingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingTime |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UndelegationInfo", 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 m.UndelegationInfo == nil { + m.UndelegationInfo = &BTCUndelegationInfo{} + } + if err := m.UndelegationInfo.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 skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btcstaking/types/query.pb.gw.go b/x/btcstaking/types/query.pb.gw.go new file mode 100644 index 000000000..01da4f93d --- /dev/null +++ b/x/btcstaking/types/query.pb.gw.go @@ -0,0 +1,1048 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: babylon/btcstaking/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +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 + +} + +var ( + filter_Query_FinalityProviders_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_FinalityProviders_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProvidersRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FinalityProviders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.FinalityProviders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FinalityProviders_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProvidersRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FinalityProviders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.FinalityProviders(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_FinalityProvider_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := client.FinalityProvider(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FinalityProvider_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := server.FinalityProvider(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_BTCDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_BTCDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCDelegationsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_BTCDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.BTCDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BTCDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCDelegationsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_BTCDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.BTCDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ActiveFinalityProvidersAtHeight_0 = &utilities.DoubleArray{Encoding: map[string]int{"height": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ActiveFinalityProvidersAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryActiveFinalityProvidersAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ActiveFinalityProvidersAtHeight_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ActiveFinalityProvidersAtHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ActiveFinalityProvidersAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryActiveFinalityProvidersAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ActiveFinalityProvidersAtHeight_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ActiveFinalityProvidersAtHeight(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_FinalityProviderPowerAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderPowerAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.FinalityProviderPowerAtHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FinalityProviderPowerAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderPowerAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.FinalityProviderPowerAtHeight(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_FinalityProviderCurrentPower_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderCurrentPowerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := client.FinalityProviderCurrentPower(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FinalityProviderCurrentPower_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderCurrentPowerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := server.FinalityProviderCurrentPower(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ActivatedHeight_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryActivatedHeightRequest + var metadata runtime.ServerMetadata + + msg, err := client.ActivatedHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ActivatedHeight_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryActivatedHeightRequest + var metadata runtime.ServerMetadata + + msg, err := server.ActivatedHeight(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_FinalityProviderDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"fp_btc_pk_hex": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_FinalityProviderDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FinalityProviderDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.FinalityProviderDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FinalityProviderDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFinalityProviderDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FinalityProviderDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.FinalityProviderDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BTCDelegation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["staking_tx_hash_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "staking_tx_hash_hex") + } + + protoReq.StakingTxHashHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "staking_tx_hash_hex", err) + } + + msg, err := client.BTCDelegation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BTCDelegation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["staking_tx_hash_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "staking_tx_hash_hex") + } + + protoReq.StakingTxHashHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "staking_tx_hash_hex", err) + } + + msg, err := server.BTCDelegation(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// 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_FinalityProviders_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_FinalityProviders_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_FinalityProviders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProvider_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_FinalityProvider_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_FinalityProvider_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCDelegations_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_BTCDelegations_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_BTCDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ActiveFinalityProvidersAtHeight_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_ActiveFinalityProvidersAtHeight_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_ActiveFinalityProvidersAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderPowerAtHeight_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_FinalityProviderPowerAtHeight_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_FinalityProviderPowerAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderCurrentPower_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_FinalityProviderCurrentPower_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_FinalityProviderCurrentPower_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ActivatedHeight_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_ActivatedHeight_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_ActivatedHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderDelegations_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_FinalityProviderDelegations_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_FinalityProviderDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCDelegation_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_BTCDelegation_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_BTCDelegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "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_FinalityProviders_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_FinalityProviders_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_FinalityProviders_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProvider_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_FinalityProvider_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_FinalityProvider_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCDelegations_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_BTCDelegations_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_BTCDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ActiveFinalityProvidersAtHeight_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_ActiveFinalityProvidersAtHeight_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_ActiveFinalityProvidersAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderPowerAtHeight_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_FinalityProviderPowerAtHeight_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_FinalityProviderPowerAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderCurrentPower_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_FinalityProviderCurrentPower_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_FinalityProviderCurrentPower_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ActivatedHeight_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_ActivatedHeight_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_ActivatedHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FinalityProviderDelegations_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_FinalityProviderDelegations_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_FinalityProviderDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCDelegation_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_BTCDelegation_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_BTCDelegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btcstaking", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FinalityProviders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btcstaking", "v1", "finality_providers"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FinalityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"babylon", "btcstaking", "v1", "finality_providers", "fp_btc_pk_hex", "finality_provider"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BTCDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btcstaking", "v1", "btc_delegations"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ActiveFinalityProvidersAtHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylon", "btcstaking", "v1", "finality_providers", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FinalityProviderPowerAtHeight_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", "btcstaking", "v1", "finality_providers", "fp_btc_pk_hex", "power", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FinalityProviderCurrentPower_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"babylon", "btcstaking", "v1", "finality_providers", "fp_btc_pk_hex", "power"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ActivatedHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "btcstaking", "v1", "activated_height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FinalityProviderDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"babylon", "btcstaking", "v1", "finality_providers", "fp_btc_pk_hex", "delegations"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BTCDelegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylon", "btcstaking", "v1", "btc_delegations", "staking_tx_hash_hex"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_FinalityProviders_0 = runtime.ForwardResponseMessage + + forward_Query_FinalityProvider_0 = runtime.ForwardResponseMessage + + forward_Query_BTCDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_ActiveFinalityProvidersAtHeight_0 = runtime.ForwardResponseMessage + + forward_Query_FinalityProviderPowerAtHeight_0 = runtime.ForwardResponseMessage + + forward_Query_FinalityProviderCurrentPower_0 = runtime.ForwardResponseMessage + + forward_Query_ActivatedHeight_0 = runtime.ForwardResponseMessage + + forward_Query_FinalityProviderDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_BTCDelegation_0 = runtime.ForwardResponseMessage +) diff --git a/x/btcstaking/types/tx.pb.go b/x/btcstaking/types/tx.pb.go new file mode 100644 index 000000000..be9cf29ba --- /dev/null +++ b/x/btcstaking/types/tx.pb.go @@ -0,0 +1,3808 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btcstaking/v1/tx.proto + +package types + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + types1 "github.com/babylonchain/babylon/x/btccheckpoint/types" + _ "github.com/cosmos/cosmos-proto" + secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + types "github.com/cosmos/cosmos-sdk/x/staking/types" + _ "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. +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 + +// MsgCreateFinalityProvider is the message for creating a finality provider +type MsgCreateFinalityProvider struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // description defines the description terms for the finality provider. + Description *types.Description `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // commission defines the commission rate of finality provider. + Commission *cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=commission,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"commission,omitempty"` + // babylon_pk is the Babylon secp256k1 PK of this finality provider + BabylonPk *secp256k1.PubKey `protobuf:"bytes,4,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // btc_pk is the Bitcoin secp256k1 PK of this finality provider + // the PK follows encoding in BIP-340 spec + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,5,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // pop is the proof of possession of babylon_pk and btc_pk + Pop *ProofOfPossession `protobuf:"bytes,6,opt,name=pop,proto3" json:"pop,omitempty"` +} + +func (m *MsgCreateFinalityProvider) Reset() { *m = MsgCreateFinalityProvider{} } +func (m *MsgCreateFinalityProvider) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFinalityProvider) ProtoMessage() {} +func (*MsgCreateFinalityProvider) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{0} +} +func (m *MsgCreateFinalityProvider) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFinalityProvider) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFinalityProvider.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 *MsgCreateFinalityProvider) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFinalityProvider.Merge(m, src) +} +func (m *MsgCreateFinalityProvider) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFinalityProvider) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFinalityProvider.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFinalityProvider proto.InternalMessageInfo + +func (m *MsgCreateFinalityProvider) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgCreateFinalityProvider) GetDescription() *types.Description { + if m != nil { + return m.Description + } + return nil +} + +func (m *MsgCreateFinalityProvider) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *MsgCreateFinalityProvider) GetPop() *ProofOfPossession { + if m != nil { + return m.Pop + } + return nil +} + +// MsgCreateFinalityProviderResponse is the response for MsgCreateFinalityProvider +type MsgCreateFinalityProviderResponse struct { +} + +func (m *MsgCreateFinalityProviderResponse) Reset() { *m = MsgCreateFinalityProviderResponse{} } +func (m *MsgCreateFinalityProviderResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFinalityProviderResponse) ProtoMessage() {} +func (*MsgCreateFinalityProviderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{1} +} +func (m *MsgCreateFinalityProviderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFinalityProviderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFinalityProviderResponse.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 *MsgCreateFinalityProviderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFinalityProviderResponse.Merge(m, src) +} +func (m *MsgCreateFinalityProviderResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFinalityProviderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFinalityProviderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFinalityProviderResponse proto.InternalMessageInfo + +// MsgCreateBTCDelegation is the message for creating a BTC delegation +type MsgCreateBTCDelegation struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // babylon_pk is the Babylon secp256k1 PK of this BTC delegation + BabylonPk *secp256k1.PubKey `protobuf:"bytes,2,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"` + // pop is the proof of possession of babylon_pk and btc_pk + Pop *ProofOfPossession `protobuf:"bytes,3,opt,name=pop,proto3" json:"pop,omitempty"` + // btc_pk is the Bitcoin secp256k1 PK of the BTC delegator + BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,4,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"` + // fp_btc_pk_list is the list of Bitcoin secp256k1 PKs of the finality providers, if there is more than one + // finality provider pk it means that delegation is re-staked + FpBtcPkList []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,5,rep,name=fp_btc_pk_list,json=fpBtcPkList,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk_list,omitempty"` + // staking_time is the time lock used in staking transaction + StakingTime uint32 `protobuf:"varint,6,opt,name=staking_time,json=stakingTime,proto3" json:"staking_time,omitempty"` + // staking_value is the amount of satoshis locked in staking output + StakingValue int64 `protobuf:"varint,7,opt,name=staking_value,json=stakingValue,proto3" json:"staking_value,omitempty"` + // staking_tx is the staking tx along with the merkle proof of inclusion in btc block + StakingTx *types1.TransactionInfo `protobuf:"bytes,8,opt,name=staking_tx,json=stakingTx,proto3" json:"staking_tx,omitempty"` + // slashing_tx is the slashing tx + // Note that the tx itself does not contain signatures, which are off-chain. + SlashingTx *BTCSlashingTx `protobuf:"bytes,9,opt,name=slashing_tx,json=slashingTx,proto3,customtype=BTCSlashingTx" json:"slashing_tx,omitempty"` + // delegator_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + // It will be a part of the witness for the staking tx output. + // The staking tx output further needs signatures from covenant and finality provider in + // order to be spendable. + DelegatorSlashingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,10,opt,name=delegator_slashing_sig,json=delegatorSlashingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_slashing_sig,omitempty"` + // unbonding_time is the time lock used when funds are being unbonded. It is be used in: + // - unbonding transaction, time lock spending path + // - staking slashing transaction, change output + // - unbonding slashing transaction, change output + // It must be smaller than math.MaxUInt16 and larger that max(MinUnbondingTime, CheckpointFinalizationTimeout) + UnbondingTime uint32 `protobuf:"varint,11,opt,name=unbonding_time,json=unbondingTime,proto3" json:"unbonding_time,omitempty"` + // fields related to unbonding transaction + // unbonding_tx is a bitcoin unbonding transaction i.e transaction that spends + // staking output and sends it to the unbonding output + UnbondingTx []byte `protobuf:"bytes,12,opt,name=unbonding_tx,json=unbondingTx,proto3" json:"unbonding_tx,omitempty"` + // unbonding_value is amount of satoshis locked in unbonding output. + // NOTE: staking_value and unbonding_value could be different because of the difference between the fee for staking tx and that for unbonding + UnbondingValue int64 `protobuf:"varint,13,opt,name=unbonding_value,json=unbondingValue,proto3" json:"unbonding_value,omitempty"` + // unbonding_slashing_tx is the slashing tx which slash unbonding contract + // Note that the tx itself does not contain signatures, which are off-chain. + UnbondingSlashingTx *BTCSlashingTx `protobuf:"bytes,14,opt,name=unbonding_slashing_tx,json=unbondingSlashingTx,proto3,customtype=BTCSlashingTx" json:"unbonding_slashing_tx,omitempty"` + // delegator_unbonding_slashing_sig is the signature on the slashing tx by the delegator (i.e., SK corresponding to btc_pk). + DelegatorUnbondingSlashingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,15,opt,name=delegator_unbonding_slashing_sig,json=delegatorUnbondingSlashingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_unbonding_slashing_sig,omitempty"` +} + +func (m *MsgCreateBTCDelegation) Reset() { *m = MsgCreateBTCDelegation{} } +func (m *MsgCreateBTCDelegation) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBTCDelegation) ProtoMessage() {} +func (*MsgCreateBTCDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{2} +} +func (m *MsgCreateBTCDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBTCDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBTCDelegation.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 *MsgCreateBTCDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBTCDelegation.Merge(m, src) +} +func (m *MsgCreateBTCDelegation) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBTCDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBTCDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBTCDelegation proto.InternalMessageInfo + +func (m *MsgCreateBTCDelegation) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgCreateBTCDelegation) GetBabylonPk() *secp256k1.PubKey { + if m != nil { + return m.BabylonPk + } + return nil +} + +func (m *MsgCreateBTCDelegation) GetPop() *ProofOfPossession { + if m != nil { + return m.Pop + } + return nil +} + +func (m *MsgCreateBTCDelegation) GetStakingTime() uint32 { + if m != nil { + return m.StakingTime + } + return 0 +} + +func (m *MsgCreateBTCDelegation) GetStakingValue() int64 { + if m != nil { + return m.StakingValue + } + return 0 +} + +func (m *MsgCreateBTCDelegation) GetStakingTx() *types1.TransactionInfo { + if m != nil { + return m.StakingTx + } + return nil +} + +func (m *MsgCreateBTCDelegation) GetUnbondingTime() uint32 { + if m != nil { + return m.UnbondingTime + } + return 0 +} + +func (m *MsgCreateBTCDelegation) GetUnbondingTx() []byte { + if m != nil { + return m.UnbondingTx + } + return nil +} + +func (m *MsgCreateBTCDelegation) GetUnbondingValue() int64 { + if m != nil { + return m.UnbondingValue + } + return 0 +} + +// MsgCreateBTCDelegationResponse is the response for MsgCreateBTCDelegation +type MsgCreateBTCDelegationResponse struct { +} + +func (m *MsgCreateBTCDelegationResponse) Reset() { *m = MsgCreateBTCDelegationResponse{} } +func (m *MsgCreateBTCDelegationResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateBTCDelegationResponse) ProtoMessage() {} +func (*MsgCreateBTCDelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{3} +} +func (m *MsgCreateBTCDelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateBTCDelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateBTCDelegationResponse.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 *MsgCreateBTCDelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateBTCDelegationResponse.Merge(m, src) +} +func (m *MsgCreateBTCDelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateBTCDelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateBTCDelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateBTCDelegationResponse proto.InternalMessageInfo + +// MsgAddCovenantSigs is the message for handling signatures from a covenant member +type MsgAddCovenantSigs struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // pk is the BTC public key of the covenant member + Pk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=pk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"pk,omitempty"` + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + StakingTxHash string `protobuf:"bytes,3,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"` + // sigs is a list of adaptor signatures of the covenant + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + SlashingTxSigs [][]byte `protobuf:"bytes,4,rep,name=slashing_tx_sigs,json=slashingTxSigs,proto3" json:"slashing_tx_sigs,omitempty"` + // unbonding_tx_sig is the signature of the covenant on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + UnbondingTxSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,5,opt,name=unbonding_tx_sig,json=unbondingTxSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"unbonding_tx_sig,omitempty"` + // slashing_unbonding_tx_sigs is a list of adaptor signatures of the covenant + // on slashing tx corresponding to unbonding tx submitted to babylon + // the order of sigs should respect the order of finality providers + // of the corresponding delegation + SlashingUnbondingTxSigs [][]byte `protobuf:"bytes,6,rep,name=slashing_unbonding_tx_sigs,json=slashingUnbondingTxSigs,proto3" json:"slashing_unbonding_tx_sigs,omitempty"` +} + +func (m *MsgAddCovenantSigs) Reset() { *m = MsgAddCovenantSigs{} } +func (m *MsgAddCovenantSigs) String() string { return proto.CompactTextString(m) } +func (*MsgAddCovenantSigs) ProtoMessage() {} +func (*MsgAddCovenantSigs) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{4} +} +func (m *MsgAddCovenantSigs) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddCovenantSigs) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddCovenantSigs.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 *MsgAddCovenantSigs) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddCovenantSigs.Merge(m, src) +} +func (m *MsgAddCovenantSigs) XXX_Size() int { + return m.Size() +} +func (m *MsgAddCovenantSigs) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddCovenantSigs.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddCovenantSigs proto.InternalMessageInfo + +func (m *MsgAddCovenantSigs) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgAddCovenantSigs) GetStakingTxHash() string { + if m != nil { + return m.StakingTxHash + } + return "" +} + +func (m *MsgAddCovenantSigs) GetSlashingTxSigs() [][]byte { + if m != nil { + return m.SlashingTxSigs + } + return nil +} + +func (m *MsgAddCovenantSigs) GetSlashingUnbondingTxSigs() [][]byte { + if m != nil { + return m.SlashingUnbondingTxSigs + } + return nil +} + +// MsgAddCovenantSigsResponse is the response for MsgAddCovenantSigs +type MsgAddCovenantSigsResponse struct { +} + +func (m *MsgAddCovenantSigsResponse) Reset() { *m = MsgAddCovenantSigsResponse{} } +func (m *MsgAddCovenantSigsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAddCovenantSigsResponse) ProtoMessage() {} +func (*MsgAddCovenantSigsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{5} +} +func (m *MsgAddCovenantSigsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddCovenantSigsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddCovenantSigsResponse.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 *MsgAddCovenantSigsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddCovenantSigsResponse.Merge(m, src) +} +func (m *MsgAddCovenantSigsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAddCovenantSigsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddCovenantSigsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddCovenantSigsResponse proto.InternalMessageInfo + +// MsgBTCUndelegate is the message for handling signature on unbonding tx +// from its delegator. This signature effectively proves that the delegator +// wants to unbond this BTC delegation +type MsgBTCUndelegate struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + StakingTxHash string `protobuf:"bytes,2,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"` + // unbonding_tx_sig is the signature of the staker on the unbonding tx submitted to babylon + // the signature follows encoding in BIP-340 spec + UnbondingTxSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,3,opt,name=unbonding_tx_sig,json=unbondingTxSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"unbonding_tx_sig,omitempty"` +} + +func (m *MsgBTCUndelegate) Reset() { *m = MsgBTCUndelegate{} } +func (m *MsgBTCUndelegate) String() string { return proto.CompactTextString(m) } +func (*MsgBTCUndelegate) ProtoMessage() {} +func (*MsgBTCUndelegate) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{6} +} +func (m *MsgBTCUndelegate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBTCUndelegate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBTCUndelegate.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 *MsgBTCUndelegate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBTCUndelegate.Merge(m, src) +} +func (m *MsgBTCUndelegate) XXX_Size() int { + return m.Size() +} +func (m *MsgBTCUndelegate) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBTCUndelegate.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBTCUndelegate proto.InternalMessageInfo + +func (m *MsgBTCUndelegate) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgBTCUndelegate) GetStakingTxHash() string { + if m != nil { + return m.StakingTxHash + } + return "" +} + +// MsgBTCUndelegateResponse is the response for MsgBTCUndelegate +type MsgBTCUndelegateResponse struct { +} + +func (m *MsgBTCUndelegateResponse) Reset() { *m = MsgBTCUndelegateResponse{} } +func (m *MsgBTCUndelegateResponse) String() string { return proto.CompactTextString(m) } +func (*MsgBTCUndelegateResponse) ProtoMessage() {} +func (*MsgBTCUndelegateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{7} +} +func (m *MsgBTCUndelegateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBTCUndelegateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBTCUndelegateResponse.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 *MsgBTCUndelegateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBTCUndelegateResponse.Merge(m, src) +} +func (m *MsgBTCUndelegateResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgBTCUndelegateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBTCUndelegateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBTCUndelegateResponse proto.InternalMessageInfo + +// MsgSelectiveSlashingEvidence is the message for handling evidence of selective slashing +// launched by a finality provider +type MsgSelectiveSlashingEvidence struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // staking_tx_hash is the hash of the staking tx. + // It uniquely identifies a BTC delegation + StakingTxHash string `protobuf:"bytes,2,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"` + // recovered_fp_btc_sk is the BTC SK of the finality provider who + // launches the selective slashing offence. The SK is recovered by + // using a covenant adaptor signature and the corresponding Schnorr + // signature + RecoveredFpBtcSk []byte `protobuf:"bytes,3,opt,name=recovered_fp_btc_sk,json=recoveredFpBtcSk,proto3" json:"recovered_fp_btc_sk,omitempty"` +} + +func (m *MsgSelectiveSlashingEvidence) Reset() { *m = MsgSelectiveSlashingEvidence{} } +func (m *MsgSelectiveSlashingEvidence) String() string { return proto.CompactTextString(m) } +func (*MsgSelectiveSlashingEvidence) ProtoMessage() {} +func (*MsgSelectiveSlashingEvidence) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{8} +} +func (m *MsgSelectiveSlashingEvidence) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSelectiveSlashingEvidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSelectiveSlashingEvidence.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 *MsgSelectiveSlashingEvidence) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSelectiveSlashingEvidence.Merge(m, src) +} +func (m *MsgSelectiveSlashingEvidence) XXX_Size() int { + return m.Size() +} +func (m *MsgSelectiveSlashingEvidence) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSelectiveSlashingEvidence.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSelectiveSlashingEvidence proto.InternalMessageInfo + +func (m *MsgSelectiveSlashingEvidence) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgSelectiveSlashingEvidence) GetStakingTxHash() string { + if m != nil { + return m.StakingTxHash + } + return "" +} + +func (m *MsgSelectiveSlashingEvidence) GetRecoveredFpBtcSk() []byte { + if m != nil { + return m.RecoveredFpBtcSk + } + return nil +} + +// MsgSelectiveSlashingEvidenceResponse is the response for MsgSelectiveSlashingEvidence +type MsgSelectiveSlashingEvidenceResponse struct { +} + +func (m *MsgSelectiveSlashingEvidenceResponse) Reset() { *m = MsgSelectiveSlashingEvidenceResponse{} } +func (m *MsgSelectiveSlashingEvidenceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSelectiveSlashingEvidenceResponse) ProtoMessage() {} +func (*MsgSelectiveSlashingEvidenceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4baddb53e97f38f2, []int{9} +} +func (m *MsgSelectiveSlashingEvidenceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSelectiveSlashingEvidenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSelectiveSlashingEvidenceResponse.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 *MsgSelectiveSlashingEvidenceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSelectiveSlashingEvidenceResponse.Merge(m, src) +} +func (m *MsgSelectiveSlashingEvidenceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSelectiveSlashingEvidenceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSelectiveSlashingEvidenceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSelectiveSlashingEvidenceResponse proto.InternalMessageInfo + +// MsgUpdateParams defines a message for updating btcstaking 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 finality 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_4baddb53e97f38f2, []int{10} +} +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_4baddb53e97f38f2, []int{11} +} +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((*MsgCreateFinalityProvider)(nil), "babylon.btcstaking.v1.MsgCreateFinalityProvider") + proto.RegisterType((*MsgCreateFinalityProviderResponse)(nil), "babylon.btcstaking.v1.MsgCreateFinalityProviderResponse") + proto.RegisterType((*MsgCreateBTCDelegation)(nil), "babylon.btcstaking.v1.MsgCreateBTCDelegation") + proto.RegisterType((*MsgCreateBTCDelegationResponse)(nil), "babylon.btcstaking.v1.MsgCreateBTCDelegationResponse") + proto.RegisterType((*MsgAddCovenantSigs)(nil), "babylon.btcstaking.v1.MsgAddCovenantSigs") + proto.RegisterType((*MsgAddCovenantSigsResponse)(nil), "babylon.btcstaking.v1.MsgAddCovenantSigsResponse") + proto.RegisterType((*MsgBTCUndelegate)(nil), "babylon.btcstaking.v1.MsgBTCUndelegate") + proto.RegisterType((*MsgBTCUndelegateResponse)(nil), "babylon.btcstaking.v1.MsgBTCUndelegateResponse") + proto.RegisterType((*MsgSelectiveSlashingEvidence)(nil), "babylon.btcstaking.v1.MsgSelectiveSlashingEvidence") + proto.RegisterType((*MsgSelectiveSlashingEvidenceResponse)(nil), "babylon.btcstaking.v1.MsgSelectiveSlashingEvidenceResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "babylon.btcstaking.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.btcstaking.v1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("babylon/btcstaking/v1/tx.proto", fileDescriptor_4baddb53e97f38f2) } + +var fileDescriptor_4baddb53e97f38f2 = []byte{ + // 1224 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x8f, 0xd3, 0xc6, + 0x17, 0x5f, 0x27, 0x9b, 0xf0, 0xdd, 0x97, 0x4d, 0x76, 0xbf, 0x06, 0x16, 0xaf, 0x0b, 0x49, 0x08, + 0x14, 0x02, 0xea, 0x3a, 0x64, 0x29, 0xa8, 0x05, 0xa9, 0x12, 0xd9, 0x05, 0x81, 0x4a, 0xd4, 0xc8, + 0xc9, 0xf6, 0xd0, 0x1e, 0x22, 0xc7, 0x99, 0x38, 0xa3, 0x24, 0x1e, 0xcb, 0x33, 0x89, 0x12, 0xf5, + 0x52, 0xa1, 0x5e, 0x2b, 0xf5, 0xd4, 0x43, 0x6f, 0xfd, 0x0f, 0x38, 0xf0, 0x27, 0xf4, 0xc0, 0x11, + 0x71, 0xaa, 0xb6, 0xd2, 0xaa, 0x82, 0x4a, 0xfc, 0x05, 0xbd, 0x57, 0xb6, 0xc7, 0x3f, 0x92, 0xc6, + 0x85, 0x65, 0xb9, 0xc5, 0x33, 0x9f, 0xf7, 0x79, 0xef, 0x7d, 0xde, 0x7b, 0x33, 0x13, 0xc8, 0x77, + 0xb4, 0xce, 0x6c, 0x48, 0xcc, 0x4a, 0x87, 0xe9, 0x94, 0x69, 0x03, 0x6c, 0x1a, 0x95, 0x49, 0xb5, + 0xc2, 0xa6, 0x8a, 0x65, 0x13, 0x46, 0xc4, 0xb3, 0x7c, 0x5f, 0x09, 0xf7, 0x95, 0x49, 0x55, 0x3e, + 0x63, 0x10, 0x83, 0xb8, 0x88, 0x8a, 0xf3, 0xcb, 0x03, 0xcb, 0xdb, 0x3a, 0xa1, 0x23, 0x42, 0xdb, + 0xde, 0x86, 0xf7, 0xc1, 0xb7, 0xce, 0x79, 0x5f, 0x95, 0x11, 0x75, 0xf9, 0x47, 0xd4, 0xe0, 0x1b, + 0x25, 0xbe, 0xa1, 0xdb, 0x33, 0x8b, 0x91, 0x0a, 0x45, 0xba, 0xb5, 0x7b, 0xeb, 0xf6, 0xa0, 0x5a, + 0x19, 0xa0, 0x99, 0x6f, 0x5c, 0x5a, 0x1e, 0xa4, 0xa5, 0xd9, 0xda, 0xc8, 0xc7, 0x7c, 0x12, 0xc1, + 0xe8, 0x7d, 0xa4, 0x0f, 0x2c, 0x82, 0x4d, 0xe6, 0xc0, 0xe6, 0x16, 0x38, 0xfa, 0x32, 0xf7, 0x1a, + 0xb2, 0x75, 0x10, 0xd3, 0xaa, 0xfe, 0x37, 0x47, 0x15, 0x62, 0xfc, 0x12, 0xcb, 0x03, 0x94, 0x7e, + 0x4d, 0xc2, 0x76, 0x9d, 0x1a, 0x7b, 0x36, 0xd2, 0x18, 0x7a, 0x80, 0x4d, 0x6d, 0x88, 0xd9, 0xac, + 0x61, 0x93, 0x09, 0xee, 0x22, 0x5b, 0xdc, 0x82, 0x34, 0xc5, 0x86, 0x89, 0x6c, 0x49, 0x28, 0x0a, + 0xe5, 0x35, 0x95, 0x7f, 0x89, 0xf7, 0x21, 0xd3, 0x45, 0x54, 0xb7, 0xb1, 0xc5, 0x30, 0x31, 0xa5, + 0x44, 0x51, 0x28, 0x67, 0x76, 0x2f, 0x29, 0x5c, 0xaf, 0x50, 0x65, 0x37, 0x24, 0x65, 0x3f, 0x84, + 0xaa, 0x51, 0x3b, 0xb1, 0x0e, 0xa0, 0x93, 0xd1, 0x08, 0x53, 0xea, 0xb0, 0x24, 0x1d, 0x17, 0xb5, + 0x9d, 0xc3, 0xa3, 0xc2, 0x47, 0x1e, 0x11, 0xed, 0x0e, 0x14, 0x4c, 0x2a, 0x23, 0x8d, 0xf5, 0x95, + 0xc7, 0xc8, 0xd0, 0xf4, 0xd9, 0x3e, 0xd2, 0x5f, 0x3e, 0xdb, 0x01, 0xee, 0x67, 0x1f, 0xe9, 0x6a, + 0x84, 0x40, 0xfc, 0x02, 0x80, 0xa7, 0xdb, 0xb6, 0x06, 0xd2, 0xaa, 0x1b, 0x54, 0xc1, 0x0f, 0xca, + 0xab, 0x8e, 0x12, 0x54, 0x47, 0x69, 0x8c, 0x3b, 0x5f, 0xa2, 0x99, 0xba, 0xc6, 0x4d, 0x1a, 0x03, + 0xb1, 0x0e, 0xe9, 0x0e, 0xd3, 0x1d, 0xdb, 0x54, 0x51, 0x28, 0xaf, 0xd7, 0x6e, 0x1f, 0x1e, 0x15, + 0x76, 0x0d, 0xcc, 0xfa, 0xe3, 0x8e, 0xa2, 0x93, 0x51, 0x85, 0x23, 0xf5, 0xbe, 0x86, 0x4d, 0xff, + 0xa3, 0xc2, 0x66, 0x16, 0xa2, 0x4a, 0xed, 0x51, 0xe3, 0xe6, 0xa7, 0x37, 0x38, 0x65, 0xaa, 0xc3, + 0xf4, 0xc6, 0x40, 0xbc, 0x03, 0x49, 0x8b, 0x58, 0x52, 0xda, 0x8d, 0xa3, 0xac, 0x2c, 0x6d, 0x43, + 0xa5, 0x61, 0x13, 0xd2, 0xfb, 0xaa, 0xd7, 0x20, 0x94, 0x22, 0x37, 0x0b, 0xd5, 0x31, 0xba, 0x93, + 0x79, 0xf2, 0xe6, 0xe9, 0x75, 0xae, 0x76, 0xe9, 0x12, 0x5c, 0x8c, 0x2d, 0x91, 0x8a, 0xa8, 0x45, + 0x4c, 0x8a, 0x4a, 0x7f, 0x9c, 0x82, 0xad, 0x00, 0x55, 0x6b, 0xed, 0xed, 0xa3, 0x21, 0x32, 0x34, + 0x57, 0xe6, 0xb8, 0x2a, 0xce, 0xeb, 0x95, 0x38, 0xb6, 0x5e, 0x3c, 0xc1, 0xe4, 0x7b, 0x24, 0x18, + 0xd1, 0x7a, 0xf5, 0x43, 0x68, 0xfd, 0x2d, 0xe4, 0x7a, 0x56, 0xdb, 0x63, 0x6c, 0x0f, 0x31, 0x65, + 0x52, 0xaa, 0x98, 0x3c, 0x01, 0x6d, 0xa6, 0x67, 0xd5, 0x1c, 0xe2, 0xc7, 0x98, 0x32, 0xf1, 0x22, + 0xac, 0xf3, 0x84, 0xda, 0x0c, 0x8f, 0x90, 0x5b, 0xd1, 0xac, 0x9a, 0xe1, 0x6b, 0x2d, 0x3c, 0x42, + 0xe2, 0x25, 0xc8, 0xfa, 0x90, 0x89, 0x36, 0x1c, 0x23, 0xe9, 0x54, 0x51, 0x28, 0x27, 0x55, 0xdf, + 0xee, 0x6b, 0x67, 0x4d, 0x7c, 0x08, 0x10, 0xf0, 0x4c, 0xa5, 0xff, 0xb9, 0xb2, 0x5d, 0x8b, 0xca, + 0x16, 0x19, 0xf2, 0x49, 0x55, 0x69, 0xd9, 0x9a, 0x49, 0x35, 0xdd, 0x29, 0xe1, 0x23, 0xb3, 0x47, + 0xd4, 0x35, 0xdf, 0xe1, 0x54, 0xdc, 0x85, 0x0c, 0x1d, 0x6a, 0xb4, 0xcf, 0xa9, 0xd6, 0x5c, 0x09, + 0xff, 0x7f, 0x78, 0x54, 0xc8, 0xd6, 0x5a, 0x7b, 0x4d, 0xbe, 0xd3, 0x9a, 0xaa, 0x40, 0x83, 0xdf, + 0x22, 0x81, 0xad, 0xae, 0xd7, 0x13, 0xc4, 0x6e, 0x07, 0xd6, 0x14, 0x1b, 0x12, 0xb8, 0xe6, 0x9f, + 0x1f, 0x1e, 0x15, 0x6e, 0x1d, 0x47, 0xaa, 0x26, 0x36, 0x4c, 0x8d, 0x8d, 0x6d, 0xa4, 0x9e, 0x09, + 0x88, 0x7d, 0xdf, 0x4d, 0x6c, 0x88, 0x1f, 0x43, 0x6e, 0x6c, 0x76, 0x88, 0xd9, 0x0d, 0x84, 0xcb, + 0xb8, 0xc2, 0x65, 0x83, 0x55, 0x57, 0xba, 0x8b, 0xb0, 0x1e, 0x81, 0x4d, 0xa5, 0x75, 0x27, 0x1a, + 0x35, 0x13, 0x82, 0xa6, 0xe2, 0x55, 0xd8, 0x08, 0x21, 0x9e, 0xbe, 0x59, 0x57, 0xdf, 0xd0, 0x81, + 0xa7, 0xf0, 0x7d, 0x38, 0x1b, 0x02, 0xa3, 0x0a, 0xe5, 0xe2, 0x14, 0x3a, 0x1d, 0xe0, 0xc3, 0x45, + 0xf1, 0x89, 0x00, 0xc5, 0x50, 0xab, 0x25, 0x8c, 0x8e, 0x6a, 0x1b, 0x27, 0x55, 0xed, 0x42, 0xe0, + 0xe2, 0x60, 0x31, 0x86, 0x26, 0x36, 0xe6, 0x8f, 0x80, 0x22, 0xe4, 0x97, 0x0f, 0x77, 0x30, 0xff, + 0x7f, 0x27, 0x40, 0xac, 0x53, 0xe3, 0x5e, 0xb7, 0xbb, 0x47, 0x26, 0xc8, 0xd4, 0x4c, 0xd6, 0xc4, + 0x06, 0x8d, 0x9d, 0xfd, 0x07, 0x90, 0xe0, 0x33, 0xff, 0xfe, 0x43, 0x92, 0xb0, 0x06, 0xe2, 0x15, + 0xd8, 0x08, 0x7b, 0xba, 0xdd, 0xd7, 0x68, 0xdf, 0x3b, 0xc7, 0xd5, 0x6c, 0xd0, 0xad, 0x0f, 0x35, + 0xda, 0x17, 0xcb, 0xb0, 0x19, 0xa9, 0x87, 0x23, 0x20, 0x95, 0x56, 0x9d, 0x11, 0x55, 0x73, 0x61, + 0x8f, 0xba, 0x11, 0xeb, 0xb0, 0x19, 0xed, 0x07, 0x57, 0xeb, 0xd4, 0x49, 0xb5, 0xce, 0x45, 0xda, + 0xc9, 0xe9, 0xcd, 0xbb, 0x20, 0x07, 0xe1, 0x2c, 0x7a, 0xa3, 0x52, 0xda, 0x0d, 0xec, 0x9c, 0x8f, + 0x38, 0x98, 0xb3, 0xa5, 0xf3, 0x95, 0x39, 0x0f, 0xf2, 0xbf, 0x65, 0x0f, 0xaa, 0xf2, 0x9b, 0x00, + 0x9b, 0x75, 0x6a, 0xd4, 0x5a, 0x7b, 0x07, 0x26, 0x2f, 0x37, 0x8a, 0xad, 0xc9, 0x12, 0x2d, 0x13, + 0xcb, 0xb4, 0x5c, 0xa6, 0x50, 0xf2, 0x03, 0x2b, 0x34, 0x9f, 0xa4, 0x0c, 0xd2, 0x62, 0x16, 0x41, + 0x8a, 0xbf, 0x08, 0x70, 0xbe, 0x4e, 0x8d, 0x26, 0x1a, 0x22, 0x9d, 0xe1, 0x09, 0xf2, 0x7b, 0xf8, + 0xbe, 0x73, 0x3f, 0x99, 0xfa, 0xc9, 0xd3, 0xdd, 0x81, 0xd3, 0x36, 0xd2, 0xc9, 0x04, 0xd9, 0xa8, + 0xdb, 0xe6, 0xa7, 0x3c, 0x1d, 0x78, 0x19, 0xab, 0x9b, 0xc1, 0xd6, 0x03, 0xe7, 0xc4, 0x6e, 0x0e, + 0xe6, 0x03, 0xbf, 0x02, 0x97, 0xff, 0x2b, 0xb6, 0x20, 0x89, 0x9f, 0x05, 0xd8, 0xa8, 0x53, 0xe3, + 0xc0, 0xea, 0x6a, 0x0c, 0x35, 0xdc, 0x57, 0x99, 0x78, 0x1b, 0xd6, 0xb4, 0x31, 0xeb, 0x13, 0x1b, + 0xb3, 0x99, 0x17, 0x7a, 0x4d, 0x7a, 0xf9, 0x6c, 0xe7, 0x0c, 0xbf, 0x20, 0xef, 0x75, 0xbb, 0x36, + 0xa2, 0xb4, 0xc9, 0x6c, 0x6c, 0x1a, 0x6a, 0x08, 0x15, 0xef, 0x42, 0xda, 0x7b, 0xd7, 0xf1, 0x2b, + 0xf5, 0x42, 0xdc, 0xcd, 0xe8, 0x82, 0x6a, 0xab, 0xcf, 0x8f, 0x0a, 0x2b, 0x2a, 0x37, 0xb9, 0x93, + 0x73, 0xa2, 0x0f, 0xc9, 0x4a, 0xdb, 0x70, 0x6e, 0x21, 0x2e, 0x3f, 0xe6, 0xdd, 0xbf, 0x52, 0x90, + 0xac, 0x53, 0x43, 0xfc, 0x41, 0x80, 0xad, 0x98, 0xf7, 0xdb, 0x8d, 0x18, 0xd7, 0xb1, 0xcf, 0x09, + 0xf9, 0xb3, 0xe3, 0x5a, 0xf8, 0xe1, 0x88, 0xdf, 0xc1, 0xe9, 0x65, 0x8f, 0x8f, 0x9d, 0xb7, 0x11, + 0xce, 0xc1, 0xe5, 0x5b, 0xc7, 0x82, 0x07, 0xce, 0x09, 0x6c, 0x2c, 0x9e, 0x7c, 0xd7, 0xe2, 0x99, + 0x16, 0xa0, 0x72, 0xf5, 0x9d, 0xa1, 0x81, 0x43, 0x0c, 0xd9, 0xf9, 0xa1, 0xbe, 0x1a, 0xcf, 0x31, + 0x07, 0x94, 0x2b, 0xef, 0x08, 0x0c, 0x5c, 0xfd, 0x28, 0xc0, 0x76, 0xfc, 0x74, 0xdd, 0x8c, 0xa7, + 0x8b, 0x35, 0x92, 0xef, 0xbe, 0x87, 0x51, 0x10, 0x4f, 0x0f, 0xd6, 0xe7, 0xe6, 0xe4, 0x4a, 0x3c, + 0x59, 0x14, 0x27, 0x2b, 0xef, 0x86, 0xf3, 0xfd, 0xc8, 0xa9, 0xef, 0xdf, 0x3c, 0xbd, 0x2e, 0xd4, + 0x1e, 0x3f, 0x7f, 0x95, 0x17, 0x5e, 0xbc, 0xca, 0x0b, 0x7f, 0xbe, 0xca, 0x0b, 0x3f, 0xbd, 0xce, + 0xaf, 0xbc, 0x78, 0x9d, 0x5f, 0xf9, 0xfd, 0x75, 0x7e, 0xe5, 0x9b, 0xb7, 0xde, 0x59, 0xd3, 0xe8, + 0xdf, 0x1e, 0xf7, 0xd8, 0xeb, 0xa4, 0xdd, 0xbf, 0x3d, 0x37, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, + 0x14, 0xbd, 0x7a, 0x11, 0x36, 0x0e, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// 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 { + // CreateFinalityProvider creates a new finality provider + CreateFinalityProvider(ctx context.Context, in *MsgCreateFinalityProvider, opts ...grpc.CallOption) (*MsgCreateFinalityProviderResponse, error) + // CreateBTCDelegation creates a new BTC delegation + CreateBTCDelegation(ctx context.Context, in *MsgCreateBTCDelegation, opts ...grpc.CallOption) (*MsgCreateBTCDelegationResponse, error) + // AddCovenantSigs handles signatures from a covenant member + AddCovenantSigs(ctx context.Context, in *MsgAddCovenantSigs, opts ...grpc.CallOption) (*MsgAddCovenantSigsResponse, error) + // BTCUndelegate handles a signature on unbonding tx from its delegator + BTCUndelegate(ctx context.Context, in *MsgBTCUndelegate, opts ...grpc.CallOption) (*MsgBTCUndelegateResponse, error) + // SelectiveSlashingEvidence handles the evidence of selective slashing launched + // by a finality provider + SelectiveSlashingEvidence(ctx context.Context, in *MsgSelectiveSlashingEvidence, opts ...grpc.CallOption) (*MsgSelectiveSlashingEvidenceResponse, error) + // UpdateParams updates the btcstaking module parameters. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateFinalityProvider(ctx context.Context, in *MsgCreateFinalityProvider, opts ...grpc.CallOption) (*MsgCreateFinalityProviderResponse, error) { + out := new(MsgCreateFinalityProviderResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Msg/CreateFinalityProvider", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CreateBTCDelegation(ctx context.Context, in *MsgCreateBTCDelegation, opts ...grpc.CallOption) (*MsgCreateBTCDelegationResponse, error) { + out := new(MsgCreateBTCDelegationResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Msg/CreateBTCDelegation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) AddCovenantSigs(ctx context.Context, in *MsgAddCovenantSigs, opts ...grpc.CallOption) (*MsgAddCovenantSigsResponse, error) { + out := new(MsgAddCovenantSigsResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Msg/AddCovenantSigs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) BTCUndelegate(ctx context.Context, in *MsgBTCUndelegate, opts ...grpc.CallOption) (*MsgBTCUndelegateResponse, error) { + out := new(MsgBTCUndelegateResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Msg/BTCUndelegate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SelectiveSlashingEvidence(ctx context.Context, in *MsgSelectiveSlashingEvidence, opts ...grpc.CallOption) (*MsgSelectiveSlashingEvidenceResponse, error) { + out := new(MsgSelectiveSlashingEvidenceResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.v1.Msg/SelectiveSlashingEvidence", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.btcstaking.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 { + // CreateFinalityProvider creates a new finality provider + CreateFinalityProvider(context.Context, *MsgCreateFinalityProvider) (*MsgCreateFinalityProviderResponse, error) + // CreateBTCDelegation creates a new BTC delegation + CreateBTCDelegation(context.Context, *MsgCreateBTCDelegation) (*MsgCreateBTCDelegationResponse, error) + // AddCovenantSigs handles signatures from a covenant member + AddCovenantSigs(context.Context, *MsgAddCovenantSigs) (*MsgAddCovenantSigsResponse, error) + // BTCUndelegate handles a signature on unbonding tx from its delegator + BTCUndelegate(context.Context, *MsgBTCUndelegate) (*MsgBTCUndelegateResponse, error) + // SelectiveSlashingEvidence handles the evidence of selective slashing launched + // by a finality provider + SelectiveSlashingEvidence(context.Context, *MsgSelectiveSlashingEvidence) (*MsgSelectiveSlashingEvidenceResponse, error) + // UpdateParams updates the btcstaking module parameters. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateFinalityProvider(ctx context.Context, req *MsgCreateFinalityProvider) (*MsgCreateFinalityProviderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateFinalityProvider not implemented") +} +func (*UnimplementedMsgServer) CreateBTCDelegation(ctx context.Context, req *MsgCreateBTCDelegation) (*MsgCreateBTCDelegationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateBTCDelegation not implemented") +} +func (*UnimplementedMsgServer) AddCovenantSigs(ctx context.Context, req *MsgAddCovenantSigs) (*MsgAddCovenantSigsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddCovenantSigs not implemented") +} +func (*UnimplementedMsgServer) BTCUndelegate(ctx context.Context, req *MsgBTCUndelegate) (*MsgBTCUndelegateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BTCUndelegate not implemented") +} +func (*UnimplementedMsgServer) SelectiveSlashingEvidence(ctx context.Context, req *MsgSelectiveSlashingEvidence) (*MsgSelectiveSlashingEvidenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectiveSlashingEvidence not implemented") +} +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_CreateFinalityProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateFinalityProvider) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateFinalityProvider(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Msg/CreateFinalityProvider", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateFinalityProvider(ctx, req.(*MsgCreateFinalityProvider)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CreateBTCDelegation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateBTCDelegation) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateBTCDelegation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Msg/CreateBTCDelegation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateBTCDelegation(ctx, req.(*MsgCreateBTCDelegation)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_AddCovenantSigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAddCovenantSigs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AddCovenantSigs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Msg/AddCovenantSigs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AddCovenantSigs(ctx, req.(*MsgAddCovenantSigs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_BTCUndelegate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgBTCUndelegate) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).BTCUndelegate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Msg/BTCUndelegate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).BTCUndelegate(ctx, req.(*MsgBTCUndelegate)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SelectiveSlashingEvidence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSelectiveSlashingEvidence) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SelectiveSlashingEvidence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.btcstaking.v1.Msg/SelectiveSlashingEvidence", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SelectiveSlashingEvidence(ctx, req.(*MsgSelectiveSlashingEvidence)) + } + return interceptor(ctx, in, info, handler) +} + +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.btcstaking.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.btcstaking.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateFinalityProvider", + Handler: _Msg_CreateFinalityProvider_Handler, + }, + { + MethodName: "CreateBTCDelegation", + Handler: _Msg_CreateBTCDelegation_Handler, + }, + { + MethodName: "AddCovenantSigs", + Handler: _Msg_AddCovenantSigs_Handler, + }, + { + MethodName: "BTCUndelegate", + Handler: _Msg_BTCUndelegate_Handler, + }, + { + MethodName: "SelectiveSlashingEvidence", + Handler: _Msg_SelectiveSlashingEvidence_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/btcstaking/v1/tx.proto", +} + +func (m *MsgCreateFinalityProvider) 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 *MsgCreateFinalityProvider) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFinalityProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pop != nil { + { + size, err := m.Pop.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Commission != nil { + { + size := m.Commission.Size() + i -= size + if _, err := m.Commission.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Description != nil { + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateFinalityProviderResponse) 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 *MsgCreateFinalityProviderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFinalityProviderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateBTCDelegation) 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 *MsgCreateBTCDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBTCDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DelegatorUnbondingSlashingSig != nil { + { + size := m.DelegatorUnbondingSlashingSig.Size() + i -= size + if _, err := m.DelegatorUnbondingSlashingSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7a + } + if m.UnbondingSlashingTx != nil { + { + size := m.UnbondingSlashingTx.Size() + i -= size + if _, err := m.UnbondingSlashingTx.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x72 + } + if m.UnbondingValue != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.UnbondingValue)) + i-- + dAtA[i] = 0x68 + } + if len(m.UnbondingTx) > 0 { + i -= len(m.UnbondingTx) + copy(dAtA[i:], m.UnbondingTx) + i = encodeVarintTx(dAtA, i, uint64(len(m.UnbondingTx))) + i-- + dAtA[i] = 0x62 + } + if m.UnbondingTime != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.UnbondingTime)) + i-- + dAtA[i] = 0x58 + } + if m.DelegatorSlashingSig != nil { + { + size := m.DelegatorSlashingSig.Size() + i -= size + if _, err := m.DelegatorSlashingSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + if m.SlashingTx != nil { + { + size := m.SlashingTx.Size() + i -= size + if _, err := m.SlashingTx.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.StakingTx != nil { + { + size, err := m.StakingTx.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.StakingValue != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.StakingValue)) + i-- + dAtA[i] = 0x38 + } + if m.StakingTime != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.StakingTime)) + i-- + dAtA[i] = 0x30 + } + if len(m.FpBtcPkList) > 0 { + for iNdEx := len(m.FpBtcPkList) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.FpBtcPkList[iNdEx].Size() + i -= size + if _, err := m.FpBtcPkList[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.BtcPk != nil { + { + size := m.BtcPk.Size() + i -= size + if _, err := m.BtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Pop != nil { + { + size, err := m.Pop.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.BabylonPk != nil { + { + size, err := m.BabylonPk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateBTCDelegationResponse) 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 *MsgCreateBTCDelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateBTCDelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgAddCovenantSigs) 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 *MsgAddCovenantSigs) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddCovenantSigs) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SlashingUnbondingTxSigs) > 0 { + for iNdEx := len(m.SlashingUnbondingTxSigs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SlashingUnbondingTxSigs[iNdEx]) + copy(dAtA[i:], m.SlashingUnbondingTxSigs[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.SlashingUnbondingTxSigs[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.UnbondingTxSig != nil { + { + size := m.UnbondingTxSig.Size() + i -= size + if _, err := m.UnbondingTxSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.SlashingTxSigs) > 0 { + for iNdEx := len(m.SlashingTxSigs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SlashingTxSigs[iNdEx]) + copy(dAtA[i:], m.SlashingTxSigs[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.SlashingTxSigs[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.StakingTxHash) > 0 { + i -= len(m.StakingTxHash) + copy(dAtA[i:], m.StakingTxHash) + i = encodeVarintTx(dAtA, i, uint64(len(m.StakingTxHash))) + i-- + dAtA[i] = 0x1a + } + if m.Pk != nil { + { + size := m.Pk.Size() + i -= size + if _, err := m.Pk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgAddCovenantSigsResponse) 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 *MsgAddCovenantSigsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddCovenantSigsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgBTCUndelegate) 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 *MsgBTCUndelegate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBTCUndelegate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UnbondingTxSig != nil { + { + size := m.UnbondingTxSig.Size() + i -= size + if _, err := m.UnbondingTxSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.StakingTxHash) > 0 { + i -= len(m.StakingTxHash) + copy(dAtA[i:], m.StakingTxHash) + i = encodeVarintTx(dAtA, i, uint64(len(m.StakingTxHash))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgBTCUndelegateResponse) 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 *MsgBTCUndelegateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBTCUndelegateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSelectiveSlashingEvidence) 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 *MsgSelectiveSlashingEvidence) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSelectiveSlashingEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RecoveredFpBtcSk) > 0 { + i -= len(m.RecoveredFpBtcSk) + copy(dAtA[i:], m.RecoveredFpBtcSk) + i = encodeVarintTx(dAtA, i, uint64(len(m.RecoveredFpBtcSk))) + i-- + dAtA[i] = 0x1a + } + if len(m.StakingTxHash) > 0 { + i -= len(m.StakingTxHash) + copy(dAtA[i:], m.StakingTxHash) + i = encodeVarintTx(dAtA, i, uint64(len(m.StakingTxHash))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSelectiveSlashingEvidenceResponse) 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 *MsgSelectiveSlashingEvidenceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSelectiveSlashingEvidenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +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 *MsgCreateFinalityProvider) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Description != nil { + l = m.Description.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Commission != nil { + l = m.Commission.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Pop != nil { + l = m.Pop.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateFinalityProviderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateBTCDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.BabylonPk != nil { + l = m.BabylonPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Pop != nil { + l = m.Pop.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.BtcPk != nil { + l = m.BtcPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.FpBtcPkList) > 0 { + for _, e := range m.FpBtcPkList { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.StakingTime != 0 { + n += 1 + sovTx(uint64(m.StakingTime)) + } + if m.StakingValue != 0 { + n += 1 + sovTx(uint64(m.StakingValue)) + } + if m.StakingTx != nil { + l = m.StakingTx.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.SlashingTx != nil { + l = m.SlashingTx.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.DelegatorSlashingSig != nil { + l = m.DelegatorSlashingSig.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.UnbondingTime != 0 { + n += 1 + sovTx(uint64(m.UnbondingTime)) + } + l = len(m.UnbondingTx) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.UnbondingValue != 0 { + n += 1 + sovTx(uint64(m.UnbondingValue)) + } + if m.UnbondingSlashingTx != nil { + l = m.UnbondingSlashingTx.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.DelegatorUnbondingSlashingSig != nil { + l = m.DelegatorUnbondingSlashingSig.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateBTCDelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgAddCovenantSigs) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Pk != nil { + l = m.Pk.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.StakingTxHash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.SlashingTxSigs) > 0 { + for _, b := range m.SlashingTxSigs { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + if m.UnbondingTxSig != nil { + l = m.UnbondingTxSig.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.SlashingUnbondingTxSigs) > 0 { + for _, b := range m.SlashingUnbondingTxSigs { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgAddCovenantSigsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgBTCUndelegate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.StakingTxHash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.UnbondingTxSig != nil { + l = m.UnbondingTxSig.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgBTCUndelegateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSelectiveSlashingEvidence) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.StakingTxHash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.RecoveredFpBtcSk) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSelectiveSlashingEvidenceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +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 *MsgCreateFinalityProvider) 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: MsgCreateFinalityProvider: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFinalityProvider: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", 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 m.Description == nil { + m.Description = &types.Description{} + } + if err := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", 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 + } + var v cosmossdk_io_math.LegacyDec + m.Commission = &v + if err := m.Commission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", 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 m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pop", 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 m.Pop == nil { + m.Pop = &ProofOfPossession{} + } + if err := m.Pop.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 *MsgCreateFinalityProviderResponse) 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: MsgCreateFinalityProviderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFinalityProviderResponse: 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 (m *MsgCreateBTCDelegation) 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: MsgCreateBTCDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBTCDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonPk", 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 m.BabylonPk == nil { + m.BabylonPk = &secp256k1.PubKey{} + } + if err := m.BabylonPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pop", 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 m.Pop == nil { + m.Pop = &ProofOfPossession{} + } + if err := m.Pop.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPk = &v + if err := m.BtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPkList = append(m.FpBtcPkList, v) + if err := m.FpBtcPkList[len(m.FpBtcPkList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTime", wireType) + } + m.StakingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StakingTime |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingValue", wireType) + } + m.StakingValue = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StakingValue |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTx", 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 m.StakingTx == nil { + m.StakingTx = &types1.TransactionInfo{} + } + if err := m.StakingTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v BTCSlashingTx + m.SlashingTx = &v + if err := m.SlashingTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorSlashingSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.DelegatorSlashingSig = &v + if err := m.DelegatorSlashingSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + m.UnbondingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingTime |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnbondingTx = append(m.UnbondingTx[:0], dAtA[iNdEx:postIndex]...) + if m.UnbondingTx == nil { + m.UnbondingTx = []byte{} + } + iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingValue", wireType) + } + m.UnbondingValue = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingValue |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingSlashingTx", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v BTCSlashingTx + m.UnbondingSlashingTx = &v + if err := m.UnbondingSlashingTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorUnbondingSlashingSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.DelegatorUnbondingSlashingSig = &v + if err := m.DelegatorUnbondingSlashingSig.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 *MsgCreateBTCDelegationResponse) 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: MsgCreateBTCDelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateBTCDelegationResponse: 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 (m *MsgAddCovenantSigs) 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: MsgAddCovenantSigs: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddCovenantSigs: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.Pk = &v + if err := m.Pk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHash", 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.StakingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingTxSigs", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SlashingTxSigs = append(m.SlashingTxSigs, make([]byte, postIndex-iNdEx)) + copy(m.SlashingTxSigs[len(m.SlashingTxSigs)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTxSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.UnbondingTxSig = &v + if err := m.UnbondingTxSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashingUnbondingTxSigs", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SlashingUnbondingTxSigs = append(m.SlashingUnbondingTxSigs, make([]byte, postIndex-iNdEx)) + copy(m.SlashingUnbondingTxSigs[len(m.SlashingUnbondingTxSigs)-1], dAtA[iNdEx:postIndex]) + 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 *MsgAddCovenantSigsResponse) 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: MsgAddCovenantSigsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddCovenantSigsResponse: 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 (m *MsgBTCUndelegate) 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: MsgBTCUndelegate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBTCUndelegate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHash", 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.StakingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTxSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.UnbondingTxSig = &v + if err := m.UnbondingTxSig.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 *MsgBTCUndelegateResponse) 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: MsgBTCUndelegateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBTCUndelegateResponse: 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 (m *MsgSelectiveSlashingEvidence) 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: MsgSelectiveSlashingEvidence: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSelectiveSlashingEvidence: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StakingTxHash", 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.StakingTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecoveredFpBtcSk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RecoveredFpBtcSk = append(m.RecoveredFpBtcSk[:0], dAtA[iNdEx:postIndex]...) + if m.RecoveredFpBtcSk == nil { + m.RecoveredFpBtcSk = []byte{} + } + 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 *MsgSelectiveSlashingEvidenceResponse) 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: MsgSelectiveSlashingEvidenceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSelectiveSlashingEvidenceResponse: 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 (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") +) diff --git a/x/checkpointing/README.md b/x/checkpointing/README.md new file mode 100644 index 000000000..e95bfdc6d --- /dev/null +++ b/x/checkpointing/README.md @@ -0,0 +1,371 @@ +# Checkpointing + +The Checkpointing module is responsible for generating and maintaining +the status of Babylon's Bitcoin checkpoints. The technical core +of the Checkpointing module is the [BLS signature](https://en.wikipedia.org/wiki/BLS_digital_signature) +scheme, around which this module provides the following functionalities: + +- handling requests for registering Babylon validators with their BLS keys, +- signing, verifying, and aggregating BLS signatures, +- constructing checkpoints out of the BLS signatures, and +- maintaining the status of the checkpoints. + +## Table of contents + +- [Concepts](#concepts) +- [States](#states) + - [Validator With BLS Key](#validator-with-bls-key) + - [Checkpoint](#checkpoint) + - [Genesis](#genesis) +- [Messages](#messages) + - [MsgWrappedCreateValidator](#msgwrappedcreatevalidator) +- [ABCI++](#abci) + - [PrepareProposal](#prepareproposal) + - [ProcessProposal](#processproposal) + - [ExtendVote](#extendvote) + - [VerifyVoteExtension](#verifyvoteextension) + - [PreBlock](#preblock) + - [BeginBlock](#beginblock) +- [Events](#events) +- [Queries](#queries) + +## Concepts + +Babylon checkpoints record the state of the Babylon chain at +the end of a particular [epoch](../../x/epoching/README.md). +They are created with the intention to be included in +the Bitcoin ledger to protect Babylon and +the chains that connect with it against long range attacks. +The confirmation of a Babylon checkpoint on Bitcoin +serves as an immutable record of Babylon's state +up to the checkpointed epoch and +determines the canonical branch of the Babylon chain. + +At their core, checkpoints contain a unique identifier +of the state they commit to and BLS signatures +from the validator set that corresponds to that state. +The BLS signature scheme is chosen to keep the checkpoints +verifiable and succinct, as it enables the aggregation of signatures. +To that end, each validator needs to maintain a BLS key pair +and register the BLS public key on Babylon. +Validators use their BLS private key to sign over +the block ID of the last block of the epoch and +submit their signature through an ABCI++ vote extension interface. +Valid BLS signatures are then aggregated into a checkpoint +that is included in the next block proposal. + +Once a valid checkpoint is generated, +it is checkpointed into the Bitcoin ledger through +an off-chain program +[Vigilante Submitter](https://docs.babylonchain.io/docs/developer-guides/modules/submitter). +It is responsible for constructing Bitcoin transactions that +contain outputs utilizing the +[`OP_RETURN`](https://en.bitcoin.it/wiki/OP_RETURN) script code +to include the checkpoint's data in the Bitcoin ledger. +Due to the data limitations of `OP_RETURN`, +two such transactions are constructed to contain +the whole checkpoint data. +After their inclusion, +an off-chain program called the +[Vigilante Reporter](https://docs.babylonchain.io/docs/developer-guides/modules/reporter) +submits inclusion proofs to the +[BTC Checkpoint module](../../x/btccheckpoint/README.md), +which is responsible for monitoring their confirmation status and +reporting it to the Checkpointing module. +The observation of two conflicting checkpoints with a valid BLS multi-signature +means that a fork exists and an alarm will be raised. +In this case, the Babylon chain's canonical chain is represented by +the state of the checkpoint that has been included first in the Bitcoin ledger. + +## States + +The Checkpointing module maintains the following KV stores. + +### Checkpoint + +The [checkpoint state](./keeper/ckpt_state.go) maintains all the checkpoints. +The key is the epoch number and the value is a `RawCheckpointWithMeta` +[object](../../proto/babylon/checkpointing/v1/checkpoint.proto) representing a +raw checkpoint along with some metadata. + +```protobuf +// RawCheckpoint wraps the BLS multi sig with metadata +message RawCheckpoint { + // epoch_num defines the epoch number the raw checkpoint is for + uint64 epoch_num = 1; + // block_hash defines the 'BlockID.Hash', which is the hash of + // the block that individual BLS sigs are signed on + bytes block_hash = 2 [ (gogoproto.customtype) = "BlockHash" ]; + // bitmap defines the bitmap that indicates the signers of the BLS multi sig + bytes bitmap = 3; + // bls_multi_sig defines the multi sig that is aggregated from individual BLS + // sigs + bytes bls_multi_sig = 4 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; +} + +// RawCheckpointWithMeta wraps the raw checkpoint with metadata. +message RawCheckpointWithMeta { + option (gogoproto.equal) = true; + + RawCheckpoint ckpt = 1; + // status defines the status of the checkpoint + CheckpointStatus status = 2; + // bls_aggr_pk defines the aggregated BLS public key + bytes bls_aggr_pk = 3 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/crypto/bls12381.PublicKey" ]; + // power_sum defines the accumulated voting power for the checkpoint + uint64 power_sum = 4; + // lifecycle defines the lifecycle of this checkpoint, i.e., each state + // transition and the time (in both timestamp and block height) of this + // transition. + repeated CheckpointStateUpdate lifecycle = 5; +} +``` + +### Validator with BLS key + +The [registration state](./keeper/registration_state.go) maintains +a two-way mapping between the validator address and its BLS public key. + +The Checkpoint module also stores the [validator set](../../proto/babylon/checkpointing/v1/bls_key.proto) +of every epoch with their public BLS keys. The key of the storage is the epoch +number. + +```protobuf +// ValidatorWithBLSSet defines a set of validators with their BLS public keys +message ValidatorWithBlsKeySet { repeated ValidatorWithBlsKey val_set = 1; } + +// ValidatorWithBlsKey couples validator address, voting power, and its bls +// public key +message ValidatorWithBlsKey { + // validator_address is the address of the validator + string validator_address = 1; + // bls_pub_key is the BLS public key of the validator + bytes bls_pub_key = 2; + // voting_power is the voting power of the validator at the given epoch + uint64 voting_power = 3; +} +``` + +### Genesis + +The [genesis state](./keeper/genesis_bls.go) maintains the BLS keys of the +genesis validators for the Checkpointing module. + +```protobuf +// GenesisState defines the checkpointing module's genesis state. +message GenesisState { + // genesis_keys defines the public keys for the genesis validators + repeated GenesisKey genesis_keys = 1; +} + +// GenesisKey defines public key information about the genesis validators +message GenesisKey { + // validator_address is the address corresponding to a validator + string validator_address = 1; + // bls_key defines the BLS key of the validator at genesis + BlsKey bls_key = 2; + // val_pubkey defines the ed25519 public key of the validator at genesis + cosmos.crypto.ed25519.PubKey val_pubkey = 3; +} +``` + +## Messages + +The Checkpointing module handles requests of registering Babylon validators. +The request message type is defined at +[proto/babylon/checkpointing/v1/tx.proto](../../proto/babylon/checkpointing/v1/tx.proto). +The message handler is defined at +[x/checkpointing/keeper/msg_server.go](./keeper/msg_server.go). + +### MsgWrappedCreateValidator + +The `MsgWrappedCreateValidator` message wraps the [`MsgCreateValidator`](https://github.com/cosmos/cosmos-sdk/blob/9814f684b9dd7e384064ca86876688c05e685e54/proto/cosmos/staking/v1beta1/tx.proto#L51) +defined in the staking module of the Cosmos SDK +in order to also include the BLS public key. +The message is used for registering a new validator and storing its BLS public +key. + +```protobuf +// MsgWrappedCreateValidator defines a wrapped message to create a validator +message MsgWrappedCreateValidator { + option (cosmos.msg.v1.signer) = "msg_create_validator"; + + BlsKey key = 1; + cosmos.staking.v1beta1.MsgCreateValidator msg_create_validator = 2; +} +``` + +Upon `MsgWrappedCreateValidator`, a Babylon node will execute as follows: + +1. Extract `MsgCreateValidator` and check its validity. +2. Extract the BLS public key and save it to the `address->key` and + `key->address` stores. We disallow a validator to register with different + BLS public keys or the same BLS public key being used by different + validators. +3. Enqueue the `MsgCreateValidator` to the [Epoching module](../../x/epoching/README.md) + which will handle this message at the end of the epoch as validator set + change happens per epoch. + +## Checkpointing via ABCI++ + +[ABCI++](https://docs.cometbft.com/v0.38/spec/abci/) or ABCI 2.0 is the middle +layer that controls the communication between the underlying consensus and the +application. We use ABCI++ interfaces to generate checkpoints a part +of the CometBFT consensus. Particularly, validators are responsible for +submitting a `VoteExtension` that includes their BLS signature at the end +of each epoch. The proposer of the next block builds a checkpoint by +aggregating these signatures and +injects it as a special transaction within the proposed block. +Through this, the checkpoint is stored within the application +when the block is committed to the CometBFT ledger. +The relevant handlers are defined in +[x/checkpointing/vote_ext.go](./vote_ext.go) and +[x/checkpointing/proposal.go](./proposal.go), respectively. + +### PrepareProposal + +The **PrepareProposal** method is utilized by the proposer +of the next block to construct a valid proposal. +It wraps the default `PrepareProposal` handler of the Cosmos SDK +to add a special condition that checks whether the next proposed block +will be the first block of a new epoch. +In that case, it builds a valid checkpoint using the +BLS signatures that were submitted as Vote Extensions +in the previous block by the validator set. +If a valid checkpoint cannot be built, this means +something critical is happening (e.g., invalid vote extensions or +insufficient valid BLS signatures) and the proposer should panic. + +The checkpoint is encoded as a special transaction and injected as the first +transaction of the proposed block. The format of the injected checkpoint is +defined in [x/proto/babylon/checkpointing/checkpoint.proto](../../proto/babylon/checkpointing/checkpoint.proto). +Note that the extended commit info that contains previous vote extensions is +also part of the injected checkpoint, which is used for re-constructing the +checkpoint in `ProcessProposal`. + +```protobuf +// InjectedCheckpoint wraps the checkpoint and the extended votes +message InjectedCheckpoint { + RawCheckpointWithMeta ckpt = 1; + // extended_commit_info is the commit info including the vote extensions + // from the previous proposal + tendermint.abci.ExtendedCommitInfo extended_commit_info = 2; +} +``` + +### ProcessProposal + +The **ProcessProposal** method is utilized by validators +for verifying the validity of a proposed block and +acts as one of the first steps towards achieving consensus +for the new block. +It wraps the default `ProcessProposal` handler of the Cosmos SDK +with an extension for verifying the validity of the checkpoint injected +as a special transaction in the first block of an epoch. The +verification steps on the special transaction are: + +1. extract the special transactions from the transaction set, +2. verify the vote extensions contained in the injected checkpoint, and +3. rebuild the checkpoint from the vote extensions and verify that it is + compatible with the checkpoint contained within the injected transaction. + +If any of the above steps fail, the proposal will be rejected. + +### ExtendVote + +The **ExtendVote** method is responsible for creating a BLS signature when the +validator votes for the last block of an epoch. It is invoked at the final +voting phase of a consensus round. It signs the block ID of the proposal and +constructs a vote extension which will be attached to the pre-commit vote as +opaque bytes. The format of the vote extension is defined in +[x/proto/babylon/checkpointing/bls_key.proto](../../proto/babylon/checkpointing/bls_key.proto). + +```protobuf +// VoteExtension defines the structure used to create a BLS vote extension. +message VoteExtension { + // signer is the address of the vote extension signer + string signer = 1; + // validator_address is the address of the validator + string validator_address = 2; + // block_hash is the hash of the block that the vote extension is signed over + bytes block_hash = 3; + // epoch_num is the epoch number of the vote extension + uint64 epoch_num = 4; + // height is the height of the vote extension + uint64 height =5; + // bls_sig is the BLS signature + bytes bls_sig = 6 + [ (gogoproto.customtype) = + "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; +} +``` + +### VerifyVoteExtension + +**VerifyVoteExtension** is responsible for verifying the vote extension if +the voting proposal is the last block of the current epoch. It is called +when a pre-commit vote is received. It extracts the BLS signature from +the vote extension attached to the pre-commit vote and verifies it using the +corresponding BLS public key. +If the verification fails, the relevant pre-commit vote will be rejected. + +### PreBlock + +**PreBlock** is responsible for persistently storing the checkpoint from the +special checkpoint transaction injected on the first block of the epoch. +It is called at the first step of finalizing a block. +Since the verification is already done in `ProcessProposal`, +the `PreBlock` will store the checkpoint to the application without further +checks. + +### BeginBlock + +**BeginBlock** is responsible for initiating the validator set with their +BLS public keys if the current proposal is the first block of the new epoch. +It is called right after `PreBlock` during block finalization. +It reads the validator set of the epoch from the Epoching module and +associates the validator set with their BLS public keys. The logic is defined +at [x/checkpointing/abci.go]((./abci.go)). + +## Events + +The Checkpointing module emits events when the status of checkpoints is +changed or a conflicting checkpoint is found. The events are +defined at [proto/babylon/checkpointing/v1/events.proto](../../proto/babylon/checkpointing/v1/events.proto). + +```protobuf +// EventCheckpointAccumulating is emitted when a checkpoint reaches the +// `Accumulating` state. +message EventCheckpointAccumulating { RawCheckpointWithMeta checkpoint = 1; } +// EventCheckpointSealed is emitted when a checkpoint reaches the `Sealed` +// state. +message EventCheckpointSealed { RawCheckpointWithMeta checkpoint = 1; } +// EventCheckpointSubmitted is emitted when a checkpoint reaches the `Submitted` +// state. +message EventCheckpointSubmitted { RawCheckpointWithMeta checkpoint = 1; } +// EventCheckpointConfirmed is emitted when a checkpoint reaches the `Confirmed` +// state. +message EventCheckpointConfirmed { RawCheckpointWithMeta checkpoint = 1; } +// EventCheckpointFinalized is emitted when a checkpoint reaches the `Finalized` +// state. +message EventCheckpointFinalized { RawCheckpointWithMeta checkpoint = 1; } +// EventCheckpointForgotten is emitted when a checkpoint switches to a +// `Forgotten` state. +message EventCheckpointForgotten { RawCheckpointWithMeta checkpoint = 1; } +// EventConflictingCheckpoint is emitted when two conflicting checkpoints are +// found. +message EventConflictingCheckpoint { + RawCheckpoint conflicting_checkpoint = 1; + RawCheckpointWithMeta local_checkpoint = 2; +} +``` + +## Queries + +The Checkpointing module provides a set of queries about BLS keys the status of +checkpoints, listed at +[docs.babylonchain.io](https://docs.babylonchain.io/docs/developer-guides/grpcrestapi#tag/Checkpointing). diff --git a/x/checkpointing/abci.go b/x/checkpointing/abci.go index eee07578d..15f5d4db9 100644 --- a/x/checkpointing/abci.go +++ b/x/checkpointing/abci.go @@ -1,6 +1,7 @@ package checkpointing import ( + "context" "fmt" "time" @@ -8,20 +9,14 @@ import ( "github.com/babylonchain/babylon/x/checkpointing/keeper" - abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" ) // BeginBlocker is called at the beginning of every block. -// Upon each BeginBlock, if reaching the second block after the epoch begins, then -// - extract the LastCommitHash from the block -// - create a raw checkpoint with the status of ACCUMULATING -// - start a BLS signer which creates a BLS sig transaction and distributes it to the network -func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) { +// Upon each BeginBlock, if reaching the first block after the epoch begins +// then we store the current validator set with BLS keys +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - - // if this block is the second block of an epoch epoch := k.GetEpoch(ctx) if epoch.IsFirstBlock(ctx) { err := k.InitValidatorBLSSet(ctx) @@ -29,32 +24,5 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) panic(fmt.Errorf("failed to store validator BLS set: %w", err)) } } - if epoch.IsSecondBlock(ctx) { - // note that this epochNum is obtained after the BeginBlocker of the epoching module is executed - // meaning that the epochNum has been incremented upon a new epoch - lch := ctx.BlockHeader().LastCommitHash - ckpt, err := k.BuildRawCheckpoint(ctx, epoch.EpochNumber-1, lch) - if err != nil { - panic("failed to generate a raw checkpoint") - } - - // emit BeginEpoch event - err = ctx.EventManager().EmitTypedEvent( - &types.EventCheckpointAccumulating{ - Checkpoint: ckpt, - }, - ) - if err != nil { - panic(err) - } - curValSet := k.GetValidatorSet(ctx, epoch.EpochNumber-1) - - go func() { - err := k.SendBlsSig(ctx, epoch.EpochNumber-1, lch, curValSet) - if err != nil { - // failing to send a BLS-sig causes a panicking situation - panic(err) - } - }() - } + return nil } diff --git a/x/checkpointing/client/cli/query.go b/x/checkpointing/client/cli/query.go index 76b89bb05..5b1184ad6 100644 --- a/x/checkpointing/client/cli/query.go +++ b/x/checkpointing/client/cli/query.go @@ -64,6 +64,7 @@ func CmdRawCheckpointList() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "raw-checkpoint-list") return cmd } diff --git a/x/checkpointing/client/cli/tx.go b/x/checkpointing/client/cli/tx.go index f309c0654..371caa21a 100644 --- a/x/checkpointing/client/cli/tx.go +++ b/x/checkpointing/client/cli/tx.go @@ -4,13 +4,15 @@ import ( "fmt" "os" "path/filepath" - "strconv" "strings" - "github.com/babylonchain/babylon/crypto/bls12381" + "cosmossdk.io/core/address" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + + appparams "github.com/babylonchain/babylon/app/params" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" - sdk "github.com/cosmos/cosmos-sdk/types" cosmoscli "github.com/cosmos/cosmos-sdk/x/staking/client/cli" "github.com/spf13/cobra" @@ -19,12 +21,6 @@ import ( "github.com/babylonchain/babylon/x/checkpointing/types" ) -//nolint:unused -const ( - flagPacketTimeoutTimestamp = "packet-timeout-timestamp" - listSeparator = "," -) - // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { cmd := &cobra.Command{ @@ -35,63 +31,19 @@ func GetTxCmd() *cobra.Command { RunE: client.ValidateCmd, } - cmd.AddCommand(CmdTxAddBlsSig()) - cmd.AddCommand(CmdWrappedCreateValidator()) - - return cmd -} - -func CmdTxAddBlsSig() *cobra.Command { - cmd := &cobra.Command{ - Use: "submit [epoch_number] [last_commit_hash] [bls_sig] [signer address]", - Short: "submit a BLS signature", - Args: cobra.ExactArgs(4), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - epoch_num, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - return err - } - - lch, err := types.NewLastCommitHashFromHex(args[1]) - if err != nil { - return err - } - - blsSig, err := bls12381.NewBLSSigFromHex(args[2]) - if err != nil { - return err - } - - addr, err := sdk.ValAddressFromBech32(args[3]) - if err != nil { - return err - } - - msg := types.NewMsgAddBlsSig(clientCtx.GetFromAddress(), epoch_num, lch, blsSig, addr) - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) + cmd.AddCommand(CmdWrappedCreateValidator(authcodec.NewBech32Codec(appparams.Bech32PrefixValAddr))) return cmd } -func CmdWrappedCreateValidator() *cobra.Command { - cmd := cosmoscli.NewCreateValidatorCmd() - cmd.Long = strings.TrimSpace( - string(`create-validator will create a new validator initialized +func CmdWrappedCreateValidator(valAddrCodec address.Codec) *cobra.Command { + cmd := cosmoscli.NewCreateValidatorCmd(valAddrCodec) + cmd.Long = strings.TrimSpace(`create-validator will create a new validator initialized with a self-delegation to it using the BLS key generated for the validator (e.g., via babylond create-bls-key). This command creates a MsgWrappedCreateValidator message which is a wrapper of cosmos-sdk's create validator with a pair of BLS key. The BLS key should exist in priv_validator_key.json -before running the command (e.g., via babylond create-bls-key).`)) +before running the command (e.g., via babylond create-bls-key).`) cmd.RunE = func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -99,14 +51,18 @@ before running the command (e.g., via babylond create-bls-key).`)) } txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + val, err := parseAndValidateValidatorJSON(clientCtx.Codec, args[0]) if err != nil { return err } txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) - txf, msg, err := buildWrappedCreateValidatorMsg(clientCtx, txf, cmd.Flags()) + txf, msg, err := buildWrappedCreateValidatorMsg(clientCtx, txf, cmd.Flags(), val, valAddrCodec) if err != nil { return err } diff --git a/x/checkpointing/client/cli/tx_test.go b/x/checkpointing/client/cli/tx_test.go index 98452e3bd..1f35f1106 100644 --- a/x/checkpointing/client/cli/tx_test.go +++ b/x/checkpointing/client/cli/tx_test.go @@ -1,35 +1,30 @@ package cli_test import ( - "bytes" "context" "fmt" "io" "path/filepath" "testing" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/testutil/mock" - "github.com/golang/mock/gomock" - - "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/suite" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - + sdkmath "cosmossdk.io/math" abci "github.com/cometbft/cometbft/abci/types" - tmconfig "github.com/cometbft/cometbft/config" - tmbytes "github.com/cometbft/cometbft/libs/bytes" - tmos "github.com/cometbft/cometbft/libs/os" + cmtconfig "github.com/cometbft/cometbft/config" + cmtbytes "github.com/cometbft/cometbft/libs/bytes" + cmtos "github.com/cometbft/cometbft/libs/os" rpcclient "github.com/cometbft/cometbft/rpc/client" rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" coretypes "github.com/cometbft/cometbft/rpc/core/types" - tmtypes "github.com/cometbft/cometbft/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + "github.com/stretchr/testify/suite" "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/app/params" @@ -38,23 +33,23 @@ import ( checkpointcli "github.com/babylonchain/babylon/x/checkpointing/client/cli" ) -type mockTendermintRPC struct { +type mockCometRPC struct { rpcclientmock.Client responseQuery abci.ResponseQuery } -func newMockTendermintRPC(respQuery abci.ResponseQuery) mockTendermintRPC { - return mockTendermintRPC{responseQuery: respQuery} +func newMockCometRPC(respQuery abci.ResponseQuery) mockCometRPC { + return mockCometRPC{responseQuery: respQuery} } -func (mockTendermintRPC) BroadcastTxSync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { +func (mockCometRPC) BroadcastTxSync(_ context.Context, _ cmttypes.Tx) (*coretypes.ResultBroadcastTx, error) { return &coretypes.ResultBroadcastTx{}, nil } -func (m mockTendermintRPC) ABCIQueryWithOptions( +func (m mockCometRPC) ABCIQueryWithOptions( _ context.Context, - _ string, _ tmbytes.HexBytes, + _ string, _ cmtbytes.HexBytes, _ rpcclient.ABCIQueryOptions, ) (*coretypes.ResultABCIQuery, error) { return &coretypes.ResultABCIQuery{Response: m.responseQuery}, nil @@ -64,7 +59,7 @@ type CLITestSuite struct { suite.Suite kr keyring.Keyring - encCfg params.EncodingConfig + encCfg *params.EncodingConfig baseCtx client.Context clientCtx client.Context addrs []sdk.AccAddress @@ -72,150 +67,189 @@ type CLITestSuite struct { func (s *CLITestSuite) SetupSuite() { s.encCfg = app.GetEncodingConfig() - s.kr = keyring.NewInMemory(s.encCfg.Marshaler) - ctrl := gomock.NewController(s.T()) - mockAccountRetriever := mock.NewMockAccountRetriever(ctrl) - mockAccountRetriever.EXPECT().EnsureExists(gomock.Any(), gomock.Any()).Return(nil) - mockAccountRetriever.EXPECT().GetAccountNumberSequence(gomock.Any(), gomock.Any()).Return(uint64(0), uint64(0), nil) + s.kr = keyring.NewInMemory(s.encCfg.Codec) s.baseCtx = client.Context{}. WithKeyring(s.kr). WithTxConfig(s.encCfg.TxConfig). - WithCodec(s.encCfg.Marshaler). - WithClient(mockTendermintRPC{Client: rpcclientmock.Client{}}). - WithAccountRetriever(mockAccountRetriever). + WithCodec(s.encCfg.Codec). + WithClient(mockCometRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). WithOutput(io.Discard). WithChainID("test-chain") - var outBuf bytes.Buffer ctxGen := func() client.Context { - bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) - c := newMockTendermintRPC(abci.ResponseQuery{ + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := newMockCometRPC(abci.ResponseQuery{ Value: bz, }) return s.baseCtx.WithClient(c) } - s.clientCtx = ctxGen().WithOutput(&outBuf) + s.clientCtx = ctxGen() + s.addrs = make([]sdk.AccAddress, 0) for i := 0; i < 3; i++ { - k, _, err := s.clientCtx.Keyring.NewMnemonic(fmt.Sprintf("NewWrappedValidator%v", i), keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + k, _, err := s.clientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) - pub, _ := k.GetPubKey() + pub, err := k.GetPubKey() + s.Require().NoError(err) newAddr := sdk.AccAddress(pub.Address()) s.addrs = append(s.addrs, newAddr) } } -// test cases copied from https://github.com/cosmos/cosmos-sdk/blob/dabcedce71b43161c8357d051715d0d3a0919883/x/staking/client/cli/tx_test.go#L191 +// test cases copied from https://github.com/cosmos/cosmos-sdk/blob/v0.50.1/x/staking/client/cli/tx_test.go#L163 func (s *CLITestSuite) TestCmdWrappedCreateValidator() { require := s.Require() homeDir := s.T().TempDir() - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() pvKeyFile := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777) + err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) require.NoError(err) pvStateFile := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - err = tmos.EnsureDir(filepath.Dir(pvStateFile), 0777) + err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) require.NoError(err) wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) - cmd := checkpointcli.CmdWrappedCreateValidator() + cmd := checkpointcli.CmdWrappedCreateValidator(authcodec.NewBech32Codec("cosmosvaloper")) consPrivKey := wrappedPV.GetValPrivKey() - consPubKey, err := cryptocodec.FromTmPubKeyInterface(consPrivKey.PubKey()) + consPubKey, err := cryptocodec.FromCmtPubKeyInterface(consPrivKey.PubKey()) require.NoError(err) consPubKeyBz, err := s.clientCtx.Codec.MarshalInterfaceJSON(consPubKey) require.NoError(err) require.NotNil(consPubKeyBz) + validJSON := fmt.Sprintf(` + { + "pubkey": %s, + "amount": "%dstake", + "moniker": "NewValidator", + "identity": "AFAF00C4", + "website": "https://newvalidator.io", + "security": "contact@newvalidator.io", + "details": "'Hey, I am a new validator. Please delegate!'", + "commission-rate": "0.5", + "commission-max-rate": "1.0", + "commission-max-change-rate": "0.1", + "min-self-delegation": "1" + }`, consPubKeyBz, 100) + validJSONFile := testutil.WriteToNewTempFile(s.T(), validJSON) + defer validJSONFile.Close() + + validJSONWithoutOptionalFields := fmt.Sprintf(` + { + "pubkey": %s, + "amount": "%dstake", + "moniker": "NewValidator", + "commission-rate": "0.5", + "commission-max-rate": "1.0", + "commission-max-change-rate": "0.1", + "min-self-delegation": "1" + }`, consPubKeyBz, 100) + validJSONWOOptionalFile := testutil.WriteToNewTempFile(s.T(), validJSONWithoutOptionalFields) + defer validJSONWOOptionalFile.Close() + + noAmountJSON := fmt.Sprintf(` + { + "pubkey": %s, + "moniker": "NewValidator", + "commission-rate": "0.5", + "commission-max-rate": "1.0", + "commission-max-change-rate": "0.1", + "min-self-delegation": "1" + }`, consPubKeyBz) + noAmountJSONFile := testutil.WriteToNewTempFile(s.T(), noAmountJSON) + defer noAmountJSONFile.Close() + + noPubKeyJSON := fmt.Sprintf(` + { + "amount": "%dstake", + "moniker": "NewValidator", + "commission-rate": "0.5", + "commission-max-rate": "1.0", + "commission-max-change-rate": "0.1", + "min-self-delegation": "1" + }`, 100) + noPubKeyJSONFile := testutil.WriteToNewTempFile(s.T(), noPubKeyJSON) + defer noPubKeyJSONFile.Close() + + noMonikerJSON := fmt.Sprintf(` + { + "pubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"oWg2ISpLF405Jcm2vXV+2v4fnjodh6aafuIdeoW+rUw="}, + "amount": "%dstake", + "commission-rate": "0.5", + "commission-max-rate": "1.0", + "commission-max-change-rate": "0.1", + "min-self-delegation": "1" + }`, 100) + noMonikerJSONFile := testutil.WriteToNewTempFile(s.T(), noMonikerJSON) + defer noMonikerJSONFile.Close() + testCases := []struct { name string args []string - expectErr bool - expectedCode uint32 - respType proto.Message + expectErrMsg string }{ { "invalid transaction (missing amount)", []string{ - fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), - fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), - fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), - fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), - fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), - fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), - fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), - fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + noAmountJSONFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()), fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir), }, - true, 0, nil, + "must specify amount of coins to bond", }, { "invalid transaction (missing pubkey)", []string{ - fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), - fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), - fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), - fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), - fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), - fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), - fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), - fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), - fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + noPubKeyJSONFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()), fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir), }, - true, 0, nil, + "must specify the JSON encoded pubkey", }, { "invalid transaction (missing moniker)", []string{ - fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz), - fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), - fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), - fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), - fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), - fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), - fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), - fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), - fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), - fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + noMonikerJSONFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)).String()), + fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir), + }, + "must specify the moniker name", + }, + { + "valid transaction with all fields", + []string{ + validJSONFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(10))).String()), fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir), }, - true, 0, nil, + "", }, { - "valid transaction", + "valid transaction without optional fields", []string{ - fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz), - fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), - fmt.Sprintf("--%s=NewValidator", cli.FlagMoniker), - fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), - fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), - fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), - fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), - fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), - fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), - fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), - fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + validJSONWOOptionalFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(10))).String()), fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir), }, - false, 0, &sdk.TxResponse{}, + "", }, } @@ -223,16 +257,14 @@ func (s *CLITestSuite) TestCmdWrappedCreateValidator() { tc := tc s.Run(tc.name, func() { out, err := testutilcli.ExecTestCLICmd(s.clientCtx, cmd, tc.args) - if tc.expectErr { + if tc.expectErrMsg != "" { require.Error(err) + require.Contains(err.Error(), tc.expectErrMsg) } else { require.NoError(err, "test: %s\noutput: %s", tc.name, out.String()) - err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType) + resp := &sdk.TxResponse{} + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp) require.NoError(err, out.String(), "test: %s, output\n:", tc.name, out.String()) - - txResp := tc.respType.(*sdk.TxResponse) - require.Equal(tc.expectedCode, txResp.Code, - "test: %s, output\n:", tc.name, out.String()) } }) } diff --git a/x/checkpointing/client/cli/utils.go b/x/checkpointing/client/cli/utils.go index 6a0f1479e..532435bf5 100644 --- a/x/checkpointing/client/cli/utils.go +++ b/x/checkpointing/client/cli/utils.go @@ -1,95 +1,152 @@ package cli import ( + "encoding/json" "errors" "fmt" + "os" "path/filepath" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - - flag "github.com/spf13/pflag" - + "cosmossdk.io/core/address" errorsmod "cosmossdk.io/errors" - "github.com/babylonchain/babylon/privval" - "github.com/babylonchain/babylon/x/checkpointing/types" - tmconfig "github.com/cometbft/cometbft/config" - tmos "github.com/cometbft/cometbft/libs/os" + sdkmath "cosmossdk.io/math" + cmtconfig "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" cosmoscli "github.com/cosmos/cosmos-sdk/x/staking/client/cli" staketypes "github.com/cosmos/cosmos-sdk/x/staking/types" + flag "github.com/spf13/pflag" + + "github.com/babylonchain/babylon/privval" + "github.com/babylonchain/babylon/x/checkpointing/types" ) -// copied from https://github.com/cosmos/cosmos-sdk/blob/7167371f87ae641012549922a292050562821dce/x/staking/client/cli/tx.go#L340 -func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *staketypes.MsgCreateValidator, error) { - fAmount, _ := fs.GetString(cosmoscli.FlagAmount) - amount, err := sdk.ParseCoinNormalized(fAmount) +// validator struct to define the fields of the validator +type validator struct { + Amount sdk.Coin + PubKey cryptotypes.PubKey + Moniker string + Identity string + Website string + Security string + Details string + CommissionRates staketypes.CommissionRates + MinSelfDelegation sdkmath.Int +} + +// copied from https://github.com/cosmos/cosmos-sdk/blob/v0.50.1/x/staking/client/cli/utils.go#L20 +func parseAndValidateValidatorJSON(cdc codec.Codec, path string) (validator, error) { + type internalVal struct { + Amount string `json:"amount"` + PubKey json.RawMessage `json:"pubkey"` + Moniker string `json:"moniker"` + Identity string `json:"identity,omitempty"` + Website string `json:"website,omitempty"` + Security string `json:"security,omitempty"` + Details string `json:"details,omitempty"` + CommissionRate string `json:"commission-rate"` + CommissionMaxRate string `json:"commission-max-rate"` + CommissionMaxChange string `json:"commission-max-change-rate"` + MinSelfDelegation string `json:"min-self-delegation"` + } + + contents, err := os.ReadFile(path) if err != nil { - return txf, nil, err + return validator{}, err } - valAddr := clientCtx.GetFromAddress() - pkStr, err := fs.GetString(cosmoscli.FlagPubKey) + var v internalVal + err = json.Unmarshal(contents, &v) if err != nil { - return txf, nil, err + return validator{}, err } - var pk cryptotypes.PubKey - if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &pk); err != nil { - return txf, nil, err + if v.Amount == "" { + return validator{}, fmt.Errorf("must specify amount of coins to bond") + } + amount, err := sdk.ParseCoinNormalized(v.Amount) + if err != nil { + return validator{}, err } - moniker, _ := fs.GetString(cosmoscli.FlagMoniker) - identity, _ := fs.GetString(cosmoscli.FlagIdentity) - website, _ := fs.GetString(cosmoscli.FlagWebsite) - security, _ := fs.GetString(cosmoscli.FlagSecurityContact) - details, _ := fs.GetString(cosmoscli.FlagDetails) - description := staketypes.NewDescription( - moniker, - identity, - website, - security, - details, - ) + if v.PubKey == nil { + return validator{}, fmt.Errorf("must specify the JSON encoded pubkey") + } + var pk cryptotypes.PubKey + if err := cdc.UnmarshalInterfaceJSON(v.PubKey, &pk); err != nil { + return validator{}, err + } - // get the initial validator commission parameters - rateStr, _ := fs.GetString(cosmoscli.FlagCommissionRate) - maxRateStr, _ := fs.GetString(cosmoscli.FlagCommissionMaxRate) - maxChangeRateStr, _ := fs.GetString(cosmoscli.FlagCommissionMaxChangeRate) + if v.Moniker == "" { + return validator{}, fmt.Errorf("must specify the moniker name") + } - commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + commissionRates, err := buildCommissionRates(v.CommissionRate, v.CommissionMaxRate, v.CommissionMaxChange) if err != nil { - return txf, nil, err + return validator{}, err } - // get the initial validator min self delegation - msbStr, _ := fs.GetString(cosmoscli.FlagMinSelfDelegation) - - minSelfDelegation, ok := sdk.NewIntFromString(msbStr) - if !ok { - return txf, nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer") + if v.MinSelfDelegation == "" { + return validator{}, fmt.Errorf("must specify minimum self delegation") } + minSelfDelegation, ok := sdkmath.NewIntFromString(v.MinSelfDelegation) + if !ok { + return validator{}, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer") + } + + return validator{ + Amount: amount, + PubKey: pk, + Moniker: v.Moniker, + Identity: v.Identity, + Website: v.Website, + Security: v.Security, + Details: v.Details, + CommissionRates: commissionRates, + MinSelfDelegation: minSelfDelegation, + }, nil +} + +// copied from https://github.com/cosmos/cosmos-sdk/blob/v0.50.1/x/staking/client/cli/tx.go#L382 +func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet, val validator, valAc address.Codec) (tx.Factory, *staketypes.MsgCreateValidator, error) { + valAddr := clientCtx.GetFromAddress() + description := staketypes.NewDescription( + val.Moniker, + val.Identity, + val.Website, + val.Security, + val.Details, + ) + + valStr, err := valAc.BytesToString(valAddr) + if err != nil { + return txf, nil, err + } msg, err := staketypes.NewMsgCreateValidator( - sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, + valStr, val.PubKey, val.Amount, description, val.CommissionRates, val.MinSelfDelegation, ) if err != nil { return txf, nil, err } - if err := msg.ValidateBasic(); err != nil { + if err := msg.Validate(valAc); err != nil { return txf, nil, err } genOnly, _ := fs.GetBool(flags.FlagGenerateOnly) if genOnly { ip, _ := fs.GetString(cosmoscli.FlagIP) + p2pPort, _ := fs.GetUint(cosmoscli.FlagP2PPort) nodeID, _ := fs.GetString(cosmoscli.FlagNodeID) - if nodeID != "" && ip != "" { - txf = txf.WithMemo(fmt.Sprintf("%s@%s", nodeID, ip)) + if nodeID != "" && ip != "" && p2pPort > 0 { + txf = txf.WithMemo(fmt.Sprintf("%s@%s:%d", nodeID, ip, p2pPort)) } } @@ -97,8 +154,8 @@ func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *fl } // buildWrappedCreateValidatorMsg builds a MsgWrappedCreateValidator that wraps MsgCreateValidator with BLS key -func buildWrappedCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *types.MsgWrappedCreateValidator, error) { - txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, fs) +func buildWrappedCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet, val validator, valAc address.Codec) (tx.Factory, *types.MsgWrappedCreateValidator, error) { + txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, fs, val, valAc) if err != nil { return txf, nil, err } @@ -119,22 +176,23 @@ func buildWrappedCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs return txf, wrappedMsg, nil } +// Copied from https://github.com/cosmos/cosmos-sdk/blob/v0.50.1/x/staking/client/cli/utils.go#L104 func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commission staketypes.CommissionRates, err error) { if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" { return commission, errors.New("must specify all validator commission parameters") } - rate, err := sdk.NewDecFromStr(rateStr) + rate, err := sdkmath.LegacyNewDecFromStr(rateStr) if err != nil { return commission, err } - maxRate, err := sdk.NewDecFromStr(maxRateStr) + maxRate, err := sdkmath.LegacyNewDecFromStr(maxRateStr) if err != nil { return commission, err } - maxChangeRate, err := sdk.NewDecFromStr(maxChangeRateStr) + maxChangeRate, err := sdkmath.LegacyNewDecFromStr(maxChangeRateStr) if err != nil { return commission, err } @@ -145,10 +203,10 @@ func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commiss } func getValKeyFromFile(homeDir string) (*privval.ValidatorKeys, error) { - nodeCfg := tmconfig.DefaultConfig() + nodeCfg := cmtconfig.DefaultConfig() keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - if !tmos.FileExists(keyPath) { + if !cmtos.FileExists(keyPath) { return nil, errors.New("validator key file does not exist") } wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) diff --git a/x/checkpointing/client/testutil/cli_test.go b/x/checkpointing/client/testutil/cli_test.go deleted file mode 100644 index 110b2e6a7..000000000 --- a/x/checkpointing/client/testutil/cli_test.go +++ /dev/null @@ -1 +0,0 @@ -package testutil diff --git a/x/checkpointing/client/testutil/suite.go b/x/checkpointing/client/testutil/suite.go deleted file mode 100644 index 110b2e6a7..000000000 --- a/x/checkpointing/client/testutil/suite.go +++ /dev/null @@ -1 +0,0 @@ -package testutil diff --git a/x/checkpointing/exported/exported.go b/x/checkpointing/exported/exported.go deleted file mode 100644 index eabd8aa3d..000000000 --- a/x/checkpointing/exported/exported.go +++ /dev/null @@ -1 +0,0 @@ -package exported diff --git a/x/checkpointing/genesis.go b/x/checkpointing/genesis.go index 60bbca23f..1641e7805 100644 --- a/x/checkpointing/genesis.go +++ b/x/checkpointing/genesis.go @@ -1,19 +1,19 @@ package checkpointing import ( + "context" "github.com/babylonchain/babylon/x/checkpointing/keeper" "github.com/babylonchain/babylon/x/checkpointing/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { k.SetGenBlsKeys(ctx, genState.GenesisKeys) } // ExportGenesis returns the capability module's exported genesis. -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() return genesis } diff --git a/x/checkpointing/genesis_test.go b/x/checkpointing/genesis_test.go index 5fdc738c9..ba7e05bac 100644 --- a/x/checkpointing/genesis_test.go +++ b/x/checkpointing/genesis_test.go @@ -14,12 +14,11 @@ import ( simapp "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/x/checkpointing/types" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" ) func TestInitGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) ckptKeeper := app.CheckpointingKeeper valNum := 10 @@ -27,7 +26,7 @@ func TestInitGenesis(t *testing.T) { for i := 0; i < valNum; i++ { valKeys, err := privval.NewValidatorKeys(ed25519.GenPrivKey(), bls12381.GenPrivKey()) require.NoError(t, err) - valPubkey, err := cryptocodec.FromTmPubKeyInterface(valKeys.ValPubkey) + valPubkey, err := cryptocodec.FromCmtPubKeyInterface(valKeys.ValPubkey) require.NoError(t, err) genKey, err := types.NewGenesisKey( sdk.ValAddress(valKeys.ValPubkey.Address()), diff --git a/x/checkpointing/keeper/bls_signer.go b/x/checkpointing/keeper/bls_signer.go index e6d432316..54e3ba974 100644 --- a/x/checkpointing/keeper/bls_signer.go +++ b/x/checkpointing/keeper/bls_signer.go @@ -1,16 +1,10 @@ package keeper import ( - "fmt" - "time" - - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - + "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/babylonchain/babylon/client/tx" "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/types/retry" "github.com/babylonchain/babylon/x/checkpointing/types" ) @@ -18,46 +12,24 @@ type BlsSigner interface { GetAddress() sdk.ValAddress SignMsgWithBls(msg []byte) (bls12381.Signature, error) GetBlsPubkey() (bls12381.PublicKey, error) + GetValidatorPubkey() (crypto.PubKey, error) } -// SendBlsSig prepares a BLS signature message and sends it to Tendermint -func (k Keeper) SendBlsSig(ctx sdk.Context, epochNum uint64, lch types.LastCommitHash, valSet epochingtypes.ValidatorSet) error { - // get self address - addr := k.blsSigner.GetAddress() - - // check if itself is the validator - _, _, err := valSet.FindValidatorWithIndex(addr) - if err != nil { - // only send the BLS sig when the node itself is a validator, not being a validator is not an error - return nil - } - +// SignBLS signs a BLS signature over the given information +func (k Keeper) SignBLS(epochNum uint64, blockHash types.BlockHash) (bls12381.Signature, error) { // get BLS signature by signing - signBytes := types.GetSignBytes(epochNum, lch) - blsSig, err := k.blsSigner.SignMsgWithBls(signBytes) - if err != nil { - return err - } + signBytes := types.GetSignBytes(epochNum, blockHash) + return k.blsSigner.SignMsgWithBls(signBytes) +} - // create MsgAddBlsSig message - msg := types.NewMsgAddBlsSig(k.clientCtx.GetFromAddress(), epochNum, lch, blsSig, addr) +func (k Keeper) GetBLSSignerAddress() sdk.ValAddress { + return k.blsSigner.GetAddress() +} - // keep sending the message to Tendermint until success or timeout - // TODO should read the parameters from config file - var res *sdk.TxResponse - err = retry.Do(1*time.Second, 1*time.Minute, func() error { - res, err = tx.SendMsgToTendermint(k.clientCtx, msg) - if err != nil { - return err - } - return nil - }) +func (k Keeper) GetValidatorAddress() sdk.ValAddress { + pk, err := k.blsSigner.GetValidatorPubkey() if err != nil { - ctx.Logger().Error(fmt.Sprintf("Failed to send the BLS sig tx for epoch %v: %v", epochNum, err)) - return err + panic(err) } - - ctx.Logger().Info(fmt.Sprintf("Successfully sent BLS-sig tx for epoch %d, tx hash: %s, gas used: %d, gas wanted: %d", epochNum, res.TxHash, res.GasUsed, res.GasWanted)) - - return nil + return sdk.ValAddress(pk.Address()) } diff --git a/x/checkpointing/keeper/ckpt_state.go b/x/checkpointing/keeper/ckpt_state.go index afe8093de..3cc30b4e2 100644 --- a/x/checkpointing/keeper/ckpt_state.go +++ b/x/checkpointing/keeper/ckpt_state.go @@ -1,23 +1,26 @@ package keeper import ( + "context" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" "github.com/babylonchain/babylon/x/checkpointing/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) type CheckpointsState struct { cdc codec.BinaryCodec - checkpoints sdk.KVStore + checkpoints storetypes.KVStore } -func (k Keeper) CheckpointsState(ctx sdk.Context) CheckpointsState { +func (k Keeper) CheckpointsState(ctx context.Context) CheckpointsState { // Build the CheckpointsState storage - store := ctx.KVStore(k.storeKey) + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) return CheckpointsState{ cdc: k.cdc, - checkpoints: prefix.NewStore(store, types.CkptsObjectPrefix), + checkpoints: prefix.NewStore(storeAdapter, types.CkptsObjectPrefix), } } diff --git a/x/checkpointing/keeper/genesis_bls.go b/x/checkpointing/keeper/genesis_bls.go index 6983782f6..71c3cab2e 100644 --- a/x/checkpointing/keeper/genesis_bls.go +++ b/x/checkpointing/keeper/genesis_bls.go @@ -1,12 +1,13 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/checkpointing/types" sdk "github.com/cosmos/cosmos-sdk/types" ) // SetGenBlsKeys registers BLS keys with each validator at genesis -func (k Keeper) SetGenBlsKeys(ctx sdk.Context, genKeys []*types.GenesisKey) { +func (k Keeper) SetGenBlsKeys(ctx context.Context, genKeys []*types.GenesisKey) { for _, key := range genKeys { addr, err := sdk.ValAddressFromBech32(key.ValidatorAddress) if err != nil { diff --git a/x/checkpointing/keeper/grpc_query_bls_test.go b/x/checkpointing/keeper/grpc_query_bls_test.go index 322d7eb32..8ec938bd8 100644 --- a/x/checkpointing/keeper/grpc_query_bls_test.go +++ b/x/checkpointing/keeper/grpc_query_bls_test.go @@ -4,15 +4,17 @@ import ( "math/rand" "testing" + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/stretchr/testify/require" + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" checkpointingkeeper "github.com/babylonchain/babylon/x/checkpointing/keeper" "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" - "github.com/stretchr/testify/require" ) // FuzzQueryBLSKeySet does the following checks @@ -24,8 +26,9 @@ func FuzzQueryBLSKeySet(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) // a genesis validator is generated for setup - helper := testepoching.NewHelper(t) - ek := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx := helper.Ctx + ek := helper.App.EpochingKeeper ck := helper.App.CheckpointingKeeper queryHelper := baseapp.NewQueryServerTestHelper(helper.Ctx, helper.App.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, ck) @@ -35,8 +38,6 @@ func FuzzQueryBLSKeySet(f *testing.F) { genesisBLSPubkey, err := ck.GetBlsPubKey(helper.Ctx, genesisVal.Addr) require.NoError(t, err) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) epoch := ek.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) @@ -53,7 +54,8 @@ func FuzzQueryBLSKeySet(f *testing.F) { // add n new validators via MsgWrappedCreateValidator n := r.Intn(3) + 1 - addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(100000000)) + addrs, err := app.AddTestAddrs(helper.App, helper.Ctx, n, math.NewInt(100000000)) + require.NoError(t, err) wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) for i := 0; i < n; i++ { @@ -64,12 +66,10 @@ func FuzzQueryBLSKeySet(f *testing.F) { require.NoError(t, err) } - // EndBlock of block 1 - ctx = helper.EndBlock() - - // go to BeginBlock of block 11, and thus entering epoch 2 + // go to block 11, and thus entering epoch 2 for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = ek.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) diff --git a/x/checkpointing/keeper/grpc_query_checkpoint.go b/x/checkpointing/keeper/grpc_query_checkpoint.go index 94107561e..b0b2c28e8 100644 --- a/x/checkpointing/keeper/grpc_query_checkpoint.go +++ b/x/checkpointing/keeper/grpc_query_checkpoint.go @@ -160,7 +160,7 @@ func (k Keeper) LastCheckpointWithStatus(ctx context.Context, req *types.QueryLa } // GetLastCheckpointedEpoch returns the last epoch number that associates with a checkpoint -func (k Keeper) GetLastCheckpointedEpoch(ctx sdk.Context) (uint64, error) { +func (k Keeper) GetLastCheckpointedEpoch(ctx context.Context) (uint64, error) { curEpoch := k.GetEpoch(ctx).EpochNumber if curEpoch <= 0 { return 0, fmt.Errorf("current epoch should be more than 0") diff --git a/x/checkpointing/keeper/grpc_query_checkpoint_test.go b/x/checkpointing/keeper/grpc_query_checkpoint_test.go index 39a029973..ef01034a4 100644 --- a/x/checkpointing/keeper/grpc_query_checkpoint_test.go +++ b/x/checkpointing/keeper/grpc_query_checkpoint_test.go @@ -9,14 +9,12 @@ import ( "github.com/babylonchain/babylon/x/checkpointing/keeper" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/babylonchain/babylon/testutil/mocks" "github.com/babylonchain/babylon/x/checkpointing/types" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/client" "github.com/stretchr/testify/require" "github.com/babylonchain/babylon/testutil/datagen" @@ -27,8 +25,7 @@ func FuzzQueryEpoch(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil, client.Context{}) - sdkCtx := sdk.WrapSDKContext(ctx) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil) // test querying a raw checkpoint with epoch number mockCkptWithMeta := datagen.GenRandomRawCheckpointWithMeta(r) @@ -39,13 +36,13 @@ func FuzzQueryEpoch(f *testing.F) { require.NoError(t, err) ckptRequest := types.NewQueryRawCheckpointRequest(mockCkptWithMeta.Ckpt.EpochNum) - ckptResp, err := ckptKeeper.RawCheckpoint(sdkCtx, ckptRequest) + ckptResp, err := ckptKeeper.RawCheckpoint(ctx, ckptRequest) require.NoError(t, err) require.True(t, ckptResp.RawCheckpoint.Equal(mockCkptWithMeta)) // test querying the status of a given epoch number statusRequest := types.NewQueryEpochStatusRequest(mockCkptWithMeta.Ckpt.EpochNum) - statusResp, err := ckptKeeper.EpochStatus(sdkCtx, statusRequest) + statusResp, err := ckptKeeper.EpochStatus(ctx, statusRequest) require.NoError(t, err) require.Equal(t, mockCkptWithMeta.Status, statusResp.Status) }) @@ -55,8 +52,7 @@ func FuzzQueryRawCheckpoints(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil, client.Context{}) - sdkCtx := sdk.WrapSDKContext(ctx) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil) // add a random number of checkpoints checkpoints := datagen.GenRandomSequenceRawCheckpointsWithMeta(r) @@ -74,7 +70,7 @@ func FuzzQueryRawCheckpoints(f *testing.F) { pageLimit := endEpoch - startEpoch + 1 pagination := &query.PageRequest{Key: types.CkptsObjectKey(startEpoch), Limit: pageLimit} - ckptResp, err := ckptKeeper.RawCheckpoints(sdkCtx, &types.QueryRawCheckpointsRequest{Pagination: pagination}) + ckptResp, err := ckptKeeper.RawCheckpoints(ctx, &types.QueryRawCheckpointsRequest{Pagination: pagination}) require.NoError(t, err) require.Equal(t, int(pageLimit), len(ckptResp.RawCheckpoints)) require.Nil(t, ckptResp.Pagination.NextKey) @@ -96,8 +92,7 @@ func FuzzQueryStatusCount(f *testing.F) { defer ctrl.Finish() ek := mocks.NewMockEpochingKeeper(ctrl) ek.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: tipEpoch + 1}) - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil, client.Context{}) - sdkCtx := sdk.WrapSDKContext(ctx) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil) expectedCounts := make(map[string]uint64) epochCount := uint64(r.Int63n(int64(tipEpoch))) for e, ckpt := range checkpoints { @@ -117,7 +112,7 @@ func FuzzQueryStatusCount(f *testing.F) { } countRequest := types.NewQueryRecentEpochStatusCountRequest(epochCount) - resp, err := ckptKeeper.RecentEpochStatusCount(sdkCtx, countRequest) + resp, err := ckptKeeper.RecentEpochStatusCount(ctx, countRequest) require.NoError(t, err) require.Equal(t, expectedResp, resp) }) @@ -134,7 +129,7 @@ func FuzzQueryLastCheckpointWithStatus(f *testing.F) { defer ctrl.Finish() ek := mocks.NewMockEpochingKeeper(ctrl) ek.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: tipEpoch}).AnyTimes() - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil, client.Context{}) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil) checkpoints := datagen.GenSequenceRawCheckpointsWithMeta(r, tipEpoch) finalizedEpoch := datagen.RandomInt(r, int(tipEpoch)) for e := uint64(0); e < tipEpoch; e++ { @@ -177,7 +172,7 @@ func FuzzQueryRawCheckpointList(f *testing.F) { defer ctrl.Finish() ek := mocks.NewMockEpochingKeeper(ctrl) ek.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: tipEpoch}).AnyTimes() - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil, client.Context{}) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil) checkpoints := datagen.GenSequenceRawCheckpointsWithMeta(r, tipEpoch) finalizedEpoch := datagen.RandomInt(r, int(tipEpoch)) diff --git a/x/checkpointing/keeper/hooks.go b/x/checkpointing/keeper/hooks.go index fa29134d2..4e3adace7 100644 --- a/x/checkpointing/keeper/hooks.go +++ b/x/checkpointing/keeper/hooks.go @@ -1,6 +1,7 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/checkpointing/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,7 +10,7 @@ import ( var _ types.CheckpointingHooks = Keeper{} // AfterBlsKeyRegistered - call hook if registered -func (k Keeper) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error { +func (k Keeper) AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error { if k.hooks != nil { return k.hooks.AfterBlsKeyRegistered(ctx, valAddr) } @@ -17,14 +18,14 @@ func (k Keeper) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) e } // AfterRawCheckpointConfirmed - call hook if the checkpoint is confirmed -func (k Keeper) AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error { +func (k Keeper) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { if k.hooks != nil { return k.hooks.AfterRawCheckpointConfirmed(ctx, epoch) } return nil } -func (k Keeper) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *types.RawCheckpoint) error { +func (k Keeper) AfterRawCheckpointForgotten(ctx context.Context, ckpt *types.RawCheckpoint) error { if k.hooks != nil { return k.hooks.AfterRawCheckpointForgotten(ctx, ckpt) } @@ -32,7 +33,7 @@ func (k Keeper) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *types.RawChec } // AfterRawCheckpointFinalized - call hook if the checkpoint is finalized -func (k Keeper) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { +func (k Keeper) AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error { if k.hooks != nil { return k.hooks.AfterRawCheckpointFinalized(ctx, epoch) } @@ -40,7 +41,7 @@ func (k Keeper) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error } // AfterRawCheckpointBlsSigVerified - call hook if the checkpoint's BLS sig is verified -func (k Keeper) AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *types.RawCheckpoint) error { +func (k Keeper) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *types.RawCheckpoint) error { if k.hooks != nil { return k.hooks.AfterRawCheckpointBlsSigVerified(ctx, ckpt) } diff --git a/x/checkpointing/keeper/invariants.go b/x/checkpointing/keeper/invariants.go deleted file mode 100644 index b55569d4a..000000000 --- a/x/checkpointing/keeper/invariants.go +++ /dev/null @@ -1 +0,0 @@ -package keeper diff --git a/x/checkpointing/keeper/keeper.go b/x/checkpointing/keeper/keeper.go index 1de85b530..d5262b4e5 100644 --- a/x/checkpointing/keeper/keeper.go +++ b/x/checkpointing/keeper/keeper.go @@ -1,17 +1,20 @@ package keeper import ( + "context" "errors" "fmt" + corestoretypes "cosmossdk.io/core/store" + txformat "github.com/babylonchain/babylon/btctxformatter" - "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/client" + "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/x/checkpointing/types" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" @@ -20,31 +23,25 @@ import ( type ( Keeper struct { cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey + storeService corestoretypes.KVStoreService blsSigner BlsSigner epochingKeeper types.EpochingKeeper hooks types.CheckpointingHooks - clientCtx client.Context } ) func NewKeeper( cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, signer BlsSigner, ek types.EpochingKeeper, - clientCtx client.Context, ) Keeper { return Keeper{ cdc: cdc, - storeKey: storeKey, - memKey: memKey, + storeService: storeService, blsSigner: signer, epochingKeeper: ek, hooks: nil, - clientCtx: clientCtx, } } @@ -63,42 +60,42 @@ func (k *Keeper) SetHooks(sh types.CheckpointingHooks) *Keeper { return k } -// addBlsSig adds a BLS signature to the raw checkpoint and updates the status -// if sufficient signatures are accumulated for the epoch. -func (k Keeper) addBlsSig(ctx sdk.Context, sig *types.BlsSig) error { - // assuming stateless checks have done in Antehandler - - // get raw checkpoint - ckptWithMeta, err := k.GetRawCheckpoint(ctx, sig.GetEpochNum()) - if err != nil { - return err +func (k Keeper) SealCheckpoint(ctx context.Context, ckptWithMeta *types.RawCheckpointWithMeta) error { + if ckptWithMeta.Status != types.Sealed { + return fmt.Errorf("the checkpoint is not Sealed") } - // the checkpoint is not accumulating - if ckptWithMeta.Status != types.Accumulating { - return nil + sdkCtx := sdk.UnwrapSDKContext(ctx) + // record state update of Sealed + ckptWithMeta.RecordStateUpdate(ctx, types.Sealed) + // log in console + k.Logger(sdkCtx).Info(fmt.Sprintf("Checkpointing: checkpoint for epoch %v is Sealed", ckptWithMeta.Ckpt.EpochNum)) + // emit event + if err := sdkCtx.EventManager().EmitTypedEvent( + &types.EventCheckpointSealed{Checkpoint: ckptWithMeta}, + ); err != nil { + panic(fmt.Errorf("failed to emit checkpoint sealed event for epoch %v", ckptWithMeta.Ckpt.EpochNum)) } - if !sig.LastCommitHash.Equal(*ckptWithMeta.Ckpt.LastCommitHash) { - // processed BlsSig message is for invalid last commit hash - return types.ErrInvalidLastCommitHash - } + // if reaching this line, it means ckptWithMeta is updated, + // and we need to write the updated ckptWithMeta back to KVStore + return k.AddRawCheckpoint(ctx, ckptWithMeta) +} +func (k Keeper) VerifyBLSSig(ctx context.Context, sig *types.BlsSig) error { // get signer's address signerAddr, err := sdk.ValAddressFromBech32(sig.SignerAddress) if err != nil { return err } - // get validators for the epoch - vals := k.GetValidatorSet(ctx, sig.GetEpochNum()) signerBlsKey, err := k.GetBlsPubKey(ctx, signerAddr) if err != nil { return err } // verify BLS sig - signBytes := types.GetSignBytes(sig.GetEpochNum(), *sig.LastCommitHash) + signBytes := types.GetSignBytes(sig.GetEpochNum(), *sig.BlockHash) ok, err := bls12381.Verify(*sig.BlsSig, signerBlsKey, signBytes) if err != nil { return err @@ -107,40 +104,25 @@ func (k Keeper) addBlsSig(ctx sdk.Context, sig *types.BlsSig) error { return types.ErrInvalidBlsSignature } - // accumulate BLS signatures - err = ckptWithMeta.Accumulate(vals, signerAddr, signerBlsKey, *sig.BlsSig, k.GetTotalVotingPower(ctx, sig.GetEpochNum())) - if err != nil { - return err - } + return nil +} - if ckptWithMeta.Status == types.Sealed { - // emit event - err = ctx.EventManager().EmitTypedEvent( - &types.EventCheckpointSealed{Checkpoint: ckptWithMeta}, - ) - if err != nil { - k.Logger(ctx).Error("failed to emit checkpoint sealed event for epoch %v", ckptWithMeta.Ckpt.EpochNum) - } - // record state update of Sealed - ckptWithMeta.RecordStateUpdate(ctx, types.Sealed) - // log in console - k.Logger(ctx).Info(fmt.Sprintf("Checkpointing: checkpoint for epoch %v is Sealed", ckptWithMeta.Ckpt.EpochNum)) - } +func (k Keeper) GetVotingPowerByAddress(ctx context.Context, epochNum uint64, valAddr sdk.ValAddress) (int64, error) { + vals := k.GetValidatorSet(ctx, epochNum) - // if reaching this line, it means ckptWithMeta is updated, - // and we need to write the updated ckptWithMeta back to KVStore - if err = k.UpdateCheckpoint(ctx, ckptWithMeta); err != nil { - return err + v, _, err := vals.FindValidatorWithIndex(valAddr) + if err != nil { + return 0, err } - return nil + return v.Power, nil } -func (k Keeper) GetRawCheckpoint(ctx sdk.Context, epochNum uint64) (*types.RawCheckpointWithMeta, error) { +func (k Keeper) GetRawCheckpoint(ctx context.Context, epochNum uint64) (*types.RawCheckpointWithMeta, error) { return k.CheckpointsState(ctx).GetRawCkptWithMeta(epochNum) } -func (k Keeper) GetStatus(ctx sdk.Context, epochNum uint64) (types.CheckpointStatus, error) { +func (k Keeper) GetStatus(ctx context.Context, epochNum uint64) (types.CheckpointStatus, error) { ckptWithMeta, err := k.GetRawCheckpoint(ctx, epochNum) if err != nil { return -1, err @@ -149,27 +131,59 @@ func (k Keeper) GetStatus(ctx sdk.Context, epochNum uint64) (types.CheckpointSta } // AddRawCheckpoint adds a raw checkpoint into the storage -func (k Keeper) AddRawCheckpoint(ctx sdk.Context, ckptWithMeta *types.RawCheckpointWithMeta) error { +func (k Keeper) AddRawCheckpoint(ctx context.Context, ckptWithMeta *types.RawCheckpointWithMeta) error { return k.CheckpointsState(ctx).CreateRawCkptWithMeta(ckptWithMeta) } -func (k Keeper) BuildRawCheckpoint(ctx sdk.Context, epochNum uint64, lch types.LastCommitHash) (*types.RawCheckpointWithMeta, error) { - ckptWithMeta := types.NewCheckpointWithMeta(types.NewCheckpoint(epochNum, lch), types.Accumulating) +func (k Keeper) BuildRawCheckpoint(ctx context.Context, epochNum uint64, blockHash types.BlockHash) (*types.RawCheckpointWithMeta, error) { + ckptWithMeta := types.NewCheckpointWithMeta(types.NewCheckpoint(epochNum, blockHash), types.Accumulating) ckptWithMeta.RecordStateUpdate(ctx, types.Accumulating) // record the state update of Accumulating err := k.AddRawCheckpoint(ctx, ckptWithMeta) if err != nil { return nil, err } - k.Logger(ctx).Info(fmt.Sprintf("Checkpointing: a new raw checkpoint is built for epoch %v", epochNum)) + k.Logger(sdk.UnwrapSDKContext(ctx)).Info(fmt.Sprintf("Checkpointing: a new raw checkpoint is built for epoch %v", epochNum)) return ckptWithMeta, nil } +func (k Keeper) VerifyRawCheckpoint(ctx context.Context, ckpt *types.RawCheckpoint) error { + // check whether sufficient voting power is accumulated + // and verify if the multi signature is valid + totalPower := k.GetTotalVotingPower(ctx, ckpt.EpochNum) + signerSet, err := k.GetValidatorSet(ctx, ckpt.EpochNum).FindSubset(ckpt.Bitmap) + if err != nil { + return fmt.Errorf("failed to get the signer set via bitmap of epoch %d: %w", ckpt.EpochNum, err) + } + var sum int64 + signersPubKeys := make([]bls12381.PublicKey, len(signerSet)) + for i, v := range signerSet { + signersPubKeys[i], err = k.GetBlsPubKey(ctx, v.Addr) + if err != nil { + return err + } + sum += v.Power + } + if sum*3 <= totalPower*2 { + return types.ErrInvalidRawCheckpoint.Wrap("insufficient voting power") + } + msgBytes := types.GetSignBytes(ckpt.GetEpochNum(), *ckpt.BlockHash) + ok, err := bls12381.VerifyMultiSig(*ckpt.BlsMultiSig, signersPubKeys, msgBytes) + if err != nil { + return err + } + if !ok { + return types.ErrInvalidRawCheckpoint.Wrap("invalid BLS multi-sig") + } + + return nil +} + // VerifyCheckpoint verifies checkpoint from BTC. It verifies // the raw checkpoint and decides whether it is an invalid checkpoint or a // conflicting checkpoint. A conflicting checkpoint indicates the existence // of a fork -func (k Keeper) VerifyCheckpoint(ctx sdk.Context, checkpoint txformat.RawBtcCheckpoint) error { +func (k Keeper) VerifyCheckpoint(ctx context.Context, checkpoint txformat.RawBtcCheckpoint) error { _, err := k.verifyCkptBytes(ctx, &checkpoint) if err != nil { if errors.Is(err, types.ErrConflictingCheckpoint) { @@ -185,7 +199,8 @@ func (k Keeper) VerifyCheckpoint(ctx sdk.Context, checkpoint txformat.RawBtcChec // the raw checkpoint and decides whether it is an invalid checkpoint or a // conflicting checkpoint. A conflicting checkpoint indicates the existence // of a fork -func (k Keeper) verifyCkptBytes(ctx sdk.Context, rawCheckpoint *txformat.RawBtcCheckpoint) (*types.RawCheckpointWithMeta, error) { +func (k Keeper) verifyCkptBytes(ctx context.Context, rawCheckpoint *txformat.RawBtcCheckpoint) (*types.RawCheckpointWithMeta, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) ckpt, err := types.FromBTCCkptToRawCkpt(rawCheckpoint) if err != nil { return nil, fmt.Errorf("failed to decode raw checkpoint from BTC raw checkpoint: %w", err) @@ -210,33 +225,10 @@ func (k Keeper) verifyCkptBytes(ctx sdk.Context, rawCheckpoint *txformat.RawBtcC return ckptWithMeta, nil } - // next verify if the multi signature is valid - // check whether sufficient voting power is accumulated - totalPower := k.GetTotalVotingPower(ctx, ckpt.EpochNum) - signerSet, err := k.GetValidatorSet(ctx, ckpt.EpochNum).FindSubset(ckpt.Bitmap) - if err != nil { - return nil, fmt.Errorf("failed to get the signer set via bitmap of epoch %d: %w", ckpt.EpochNum, err) - } - var sum int64 - signersPubKeys := make([]bls12381.PublicKey, len(signerSet)) - for i, v := range signerSet { - signersPubKeys[i], err = k.GetBlsPubKey(ctx, v.Addr) - if err != nil { - return nil, err - } - sum += v.Power - } - if sum <= totalPower*1/3 { - return nil, types.ErrInvalidRawCheckpoint.Wrap("insufficient voting power") - } - msgBytes := types.GetSignBytes(ckpt.GetEpochNum(), *ckpt.LastCommitHash) - ok, err := bls12381.VerifyMultiSig(*ckpt.BlsMultiSig, signersPubKeys, msgBytes) - if err != nil { + // verify raw checkpoint + if err := k.VerifyRawCheckpoint(ctx, ckpt); err != nil { return nil, err } - if !ok { - return nil, types.ErrInvalidRawCheckpoint.Wrap("invalid BLS multi-sig") - } // record verified checkpoint err = k.AfterRawCheckpointBlsSigVerified(ctx, ckpt) @@ -244,17 +236,17 @@ func (k Keeper) verifyCkptBytes(ctx sdk.Context, rawCheckpoint *txformat.RawBtcC return nil, fmt.Errorf("failed to record verified checkpoint of epoch %d for monitoring: %w", ckpt.EpochNum, err) } - // now the checkpoint's multi-sig is valid, if the lastcommithash is the + // now the checkpoint's multi-sig is valid, if the BlockHash is the // same with that of the local checkpoint, it means it is valid except that // it is signed by a different signer set - if ckptWithMeta.Ckpt.LastCommitHash.Equal(*ckpt.LastCommitHash) { + if ckptWithMeta.Ckpt.BlockHash.Equal(*ckpt.BlockHash) { return ckptWithMeta, nil } // multi-sig is valid but the quorum is on a different branch, meaning conflicting is observed - k.Logger(ctx).Error(types.ErrConflictingCheckpoint.Wrapf("epoch %v", ckpt.EpochNum).Error()) + k.Logger(sdkCtx).Error(types.ErrConflictingCheckpoint.Wrapf("epoch %v", ckpt.EpochNum).Error()) // report conflicting checkpoint event - err = ctx.EventManager().EmitTypedEvent( + err = sdkCtx.EventManager().EmitTypedEvent( &types.EventConflictingCheckpoint{ ConflictingCheckpoint: ckpt, LocalCheckpoint: ckptWithMeta, @@ -273,63 +265,67 @@ func (k *Keeper) SetEpochingKeeper(ek types.EpochingKeeper) { // SetCheckpointSubmitted sets the status of a checkpoint to SUBMITTED, // and records the associated state update in lifecycle -func (k Keeper) SetCheckpointSubmitted(ctx sdk.Context, epoch uint64) { +func (k Keeper) SetCheckpointSubmitted(ctx context.Context, epoch uint64) { + sdkCtx := sdk.UnwrapSDKContext(ctx) ckpt := k.setCheckpointStatus(ctx, epoch, types.Sealed, types.Submitted) - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventCheckpointSubmitted{Checkpoint: ckpt}, ) if err != nil { - k.Logger(ctx).Error("failed to emit checkpoint submitted event for epoch %v", ckpt.Ckpt.EpochNum) + k.Logger(sdkCtx).Error("failed to emit checkpoint submitted event for epoch %v", ckpt.Ckpt.EpochNum) } } // SetCheckpointConfirmed sets the status of a checkpoint to CONFIRMED, // and records the associated state update in lifecycle -func (k Keeper) SetCheckpointConfirmed(ctx sdk.Context, epoch uint64) { +func (k Keeper) SetCheckpointConfirmed(ctx context.Context, epoch uint64) { + sdkCtx := sdk.UnwrapSDKContext(ctx) ckpt := k.setCheckpointStatus(ctx, epoch, types.Submitted, types.Confirmed) - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventCheckpointConfirmed{Checkpoint: ckpt}, ) if err != nil { - k.Logger(ctx).Error("failed to emit checkpoint confirmed event for epoch %v: %v", ckpt.Ckpt.EpochNum, err) + k.Logger(sdkCtx).Error("failed to emit checkpoint confirmed event for epoch %v: %v", ckpt.Ckpt.EpochNum, err) } // invoke hook if err := k.AfterRawCheckpointConfirmed(ctx, epoch); err != nil { - k.Logger(ctx).Error("failed to trigger checkpoint confirmed hook for epoch %v: %v", ckpt.Ckpt.EpochNum, err) + k.Logger(sdkCtx).Error("failed to trigger checkpoint confirmed hook for epoch %v: %v", ckpt.Ckpt.EpochNum, err) } } // SetCheckpointFinalized sets the status of a checkpoint to FINALIZED, // and records the associated state update in lifecycle -func (k Keeper) SetCheckpointFinalized(ctx sdk.Context, epoch uint64) { +func (k Keeper) SetCheckpointFinalized(ctx context.Context, epoch uint64) { + sdkCtx := sdk.UnwrapSDKContext(ctx) ckpt := k.setCheckpointStatus(ctx, epoch, types.Confirmed, types.Finalized) - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventCheckpointFinalized{Checkpoint: ckpt}, ) if err != nil { - k.Logger(ctx).Error("failed to emit checkpoint finalized event for epoch %v: %v", ckpt.Ckpt.EpochNum, err) + k.Logger(sdkCtx).Error("failed to emit checkpoint finalized event for epoch %v: %v", ckpt.Ckpt.EpochNum, err) } // invoke hook, which is currently subscribed by ZoneConcierge if err := k.AfterRawCheckpointFinalized(ctx, epoch); err != nil { - k.Logger(ctx).Error("failed to trigger checkpoint finalized hook for epoch %v: %v", ckpt.Ckpt.EpochNum, err) + k.Logger(sdkCtx).Error("failed to trigger checkpoint finalized hook for epoch %v: %v", ckpt.Ckpt.EpochNum, err) } } // SetCheckpointForgotten rolls back the status of a checkpoint to Sealed, // and records the associated state update in lifecycle -func (k Keeper) SetCheckpointForgotten(ctx sdk.Context, epoch uint64) { +func (k Keeper) SetCheckpointForgotten(ctx context.Context, epoch uint64) { + sdkCtx := sdk.UnwrapSDKContext(ctx) ckpt := k.setCheckpointStatus(ctx, epoch, types.Submitted, types.Sealed) - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventCheckpointForgotten{Checkpoint: ckpt}, ) if err != nil { - k.Logger(ctx).Error("failed to emit checkpoint forgotten event for epoch %v", ckpt.Ckpt.EpochNum) + k.Logger(sdkCtx).Error("failed to emit checkpoint forgotten event for epoch %v", ckpt.Ckpt.EpochNum) } } // setCheckpointStatus sets a ckptWithMeta to the given state, // and records the state update in its lifecycle -func (k Keeper) setCheckpointStatus(ctx sdk.Context, epoch uint64, from types.CheckpointStatus, to types.CheckpointStatus) *types.RawCheckpointWithMeta { +func (k Keeper) setCheckpointStatus(ctx context.Context, epoch uint64, from types.CheckpointStatus, to types.CheckpointStatus) *types.RawCheckpointWithMeta { ckptWithMeta, err := k.GetRawCheckpoint(ctx, epoch) if err != nil { // TODO: ignore err for now @@ -349,20 +345,20 @@ func (k Keeper) setCheckpointStatus(ctx sdk.Context, epoch uint64, from types.Ch panic("failed to update checkpoint status") } statusChangeMsg := fmt.Sprintf("Checkpointing: checkpoint status for epoch %v successfully changed from %v to %v", epoch, from.String(), to.String()) - k.Logger(ctx).Info(statusChangeMsg) + k.Logger(sdk.UnwrapSDKContext(ctx)).Info(statusChangeMsg) return ckptWithMeta } -func (k Keeper) UpdateCheckpoint(ctx sdk.Context, ckptWithMeta *types.RawCheckpointWithMeta) error { +func (k Keeper) UpdateCheckpoint(ctx context.Context, ckptWithMeta *types.RawCheckpointWithMeta) error { return k.CheckpointsState(ctx).UpdateCheckpoint(ckptWithMeta) } -func (k Keeper) CreateRegistration(ctx sdk.Context, blsPubKey bls12381.PublicKey, valAddr sdk.ValAddress) error { +func (k Keeper) CreateRegistration(ctx context.Context, blsPubKey bls12381.PublicKey, valAddr sdk.ValAddress) error { return k.RegistrationState(ctx).CreateRegistration(blsPubKey, valAddr) } // GetBLSPubKeySet returns the set of BLS public keys in the same order of the validator set for a given epoch -func (k Keeper) GetBLSPubKeySet(ctx sdk.Context, epochNumber uint64) ([]*types.ValidatorWithBlsKey, error) { +func (k Keeper) GetBLSPubKeySet(ctx context.Context, epochNumber uint64) ([]*types.ValidatorWithBlsKey, error) { valset := k.GetValidatorSet(ctx, epochNumber) valWithblsKeys := make([]*types.ValidatorWithBlsKey, len(valset)) for i, val := range valset { @@ -380,18 +376,22 @@ func (k Keeper) GetBLSPubKeySet(ctx sdk.Context, epochNumber uint64) ([]*types.V return valWithblsKeys, nil } -func (k Keeper) GetBlsPubKey(ctx sdk.Context, address sdk.ValAddress) (bls12381.PublicKey, error) { +func (k Keeper) GetBlsPubKey(ctx context.Context, address sdk.ValAddress) (bls12381.PublicKey, error) { return k.RegistrationState(ctx).GetBlsPubKey(address) } -func (k Keeper) GetEpoch(ctx sdk.Context) *epochingtypes.Epoch { +func (k Keeper) GetEpoch(ctx context.Context) *epochingtypes.Epoch { return k.epochingKeeper.GetEpoch(ctx) } -func (k Keeper) GetValidatorSet(ctx sdk.Context, epochNumber uint64) epochingtypes.ValidatorSet { +func (k Keeper) GetValidatorSet(ctx context.Context, epochNumber uint64) epochingtypes.ValidatorSet { return k.epochingKeeper.GetValidatorSet(ctx, epochNumber) } -func (k Keeper) GetTotalVotingPower(ctx sdk.Context, epochNumber uint64) int64 { +func (k Keeper) GetTotalVotingPower(ctx context.Context, epochNumber uint64) int64 { return k.epochingKeeper.GetTotalVotingPower(ctx, epochNumber) } + +func (k Keeper) GetPubKeyByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (cmtprotocrypto.PublicKey, error) { + return k.epochingKeeper.GetPubKeyByConsAddr(ctx, consAddr) +} diff --git a/x/checkpointing/keeper/keeper_test.go b/x/checkpointing/keeper/keeper_test.go index 84c3ec822..4dd46f58d 100644 --- a/x/checkpointing/keeper/keeper_test.go +++ b/x/checkpointing/keeper/keeper_test.go @@ -7,13 +7,11 @@ import ( "github.com/boljen/go-bitmap" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/babylonchain/babylon/btctxformatter" - "github.com/babylonchain/babylon/crypto/bls12381" - - "github.com/cosmos/cosmos-sdk/client" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "github.com/babylonchain/babylon/btctxformatter" + "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/testutil/datagen" testkeeper "github.com/babylonchain/babylon/testutil/keeper" "github.com/babylonchain/babylon/testutil/mocks" @@ -28,7 +26,7 @@ func FuzzKeeperAddRawCheckpoint(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 1) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil, client.Context{}) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil) // test nil raw checkpoint err := ckptKeeper.AddRawCheckpoint(ctx, nil) @@ -53,7 +51,7 @@ func FuzzKeeperAddRawCheckpoint(f *testing.F) { _, err = ckptKeeper.BuildRawCheckpoint( ctx, mockCkptWithMeta.Ckpt.EpochNum, - datagen.GenRandomLastCommitHash(r), + datagen.GenRandomBlockHash(r), ) require.Errorf(t, err, "raw checkpoint with the same epoch already exists") }) @@ -69,7 +67,7 @@ func FuzzKeeperSetCheckpointStatus(f *testing.F) { ctrl := gomock.NewController(t) defer ctrl.Finish() ek := mocks.NewMockEpochingKeeper(ctrl) - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil, client.Context{}) + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil) /* new accumulating checkpoint*/ mockCkptWithMeta := datagen.GenRandomRawCheckpointWithMeta(r) @@ -167,8 +165,8 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { defer ctrl.Finish() ek := mocks.NewMockEpochingKeeper(ctrl) ek.EXPECT().GetValidatorSet(gomock.Any(), gomock.Any()).Return(valSet).AnyTimes() - ek.EXPECT().GetTotalVotingPower(gomock.Any(), gomock.Any()).Return(int64(20)).AnyTimes() - ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil, client.Context{}) + ek.EXPECT().GetTotalVotingPower(gomock.Any(), gomock.Any()).Return(int64(10)).AnyTimes() + ckptKeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, ek, nil) for i, val := range valSet { err := ckptKeeper.CreateRegistration(ctx, pubkeys[i], val.Addr) require.NoError(t, err) @@ -181,7 +179,7 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { localCkptWithMeta.Status = types.Sealed localCkptWithMeta.PowerSum = 10 localCkptWithMeta.Ckpt.Bitmap = bm - msgBytes := types.GetSignBytes(localCkptWithMeta.Ckpt.EpochNum, *localCkptWithMeta.Ckpt.LastCommitHash) + msgBytes := types.GetSignBytes(localCkptWithMeta.Ckpt.EpochNum, *localCkptWithMeta.Ckpt.BlockHash) sig := bls12381.Sign(blsPrivKey1, msgBytes) localCkptWithMeta.Ckpt.BlsMultiSig = &sig _ = ckptKeeper.AddRawCheckpoint( @@ -193,7 +191,7 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { rawBtcCheckpoint := makeBtcCkptBytes( r, localCkptWithMeta.Ckpt.EpochNum, - localCkptWithMeta.Ckpt.LastCommitHash.MustMarshal(), + localCkptWithMeta.Ckpt.BlockHash.MustMarshal(), localCkptWithMeta.Ckpt.Bitmap, localCkptWithMeta.Ckpt.BlsMultiSig.Bytes(), t, @@ -206,7 +204,7 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { rawBtcCheckpoint = makeBtcCkptBytes( r, localCkptWithMeta.Ckpt.EpochNum, - localCkptWithMeta.Ckpt.LastCommitHash.MustMarshal(), + localCkptWithMeta.Ckpt.BlockHash.MustMarshal(), localCkptWithMeta.Ckpt.Bitmap, datagen.GenRandomByteArray(r, btctxformatter.BlsSigLength), t, @@ -214,13 +212,13 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { err = ckptKeeper.VerifyCheckpoint(ctx, *rawBtcCheckpoint) require.ErrorIs(t, err, types.ErrInvalidRawCheckpoint) - // 3. check a conflicting checkpoint; signed on a random lastcommithash - conflictLastCommitHash := datagen.GenRandomByteArray(r, btctxformatter.LastCommitHashLength) - msgBytes = types.GetSignBytes(localCkptWithMeta.Ckpt.EpochNum, conflictLastCommitHash) + // 3. check a conflicting checkpoint; signed on a random BlockHash + conflictBlockHash := datagen.GenRandomByteArray(r, btctxformatter.BlockHashLength) + msgBytes = types.GetSignBytes(localCkptWithMeta.Ckpt.EpochNum, conflictBlockHash) rawBtcCheckpoint = makeBtcCkptBytes( r, localCkptWithMeta.Ckpt.EpochNum, - conflictLastCommitHash, + conflictBlockHash, localCkptWithMeta.Ckpt.Bitmap, bls12381.Sign(blsPrivKey1, msgBytes), t, @@ -231,14 +229,14 @@ func FuzzKeeperCheckpointEpoch(f *testing.F) { }) } -func makeBtcCkptBytes(r *rand.Rand, epoch uint64, lch []byte, bitmap []byte, blsSig []byte, t *testing.T) *btctxformatter.RawBtcCheckpoint { +func makeBtcCkptBytes(r *rand.Rand, epoch uint64, appHash []byte, bitmap []byte, blsSig []byte, t *testing.T) *btctxformatter.RawBtcCheckpoint { tag := datagen.GenRandomByteArray(r, btctxformatter.TagLength) babylonTag := btctxformatter.BabylonTag(tag[:btctxformatter.TagLength]) address := datagen.GenRandomByteArray(r, btctxformatter.AddressLength) rawBTCCkpt := &btctxformatter.RawBtcCheckpoint{ Epoch: epoch, - LastCommitHash: lch, + BlockHash: appHash, BitMap: bitmap, SubmitterAddress: address, BlsSig: blsSig, @@ -263,7 +261,7 @@ func makeBtcCkptBytes(r *rand.Rand, epoch uint64, lch []byte, bitmap []byte, bls } func curStateUpdate(ctx sdk.Context, status types.CheckpointStatus) *types.CheckpointStateUpdate { - height, time := ctx.BlockHeight(), ctx.BlockTime() + height, time := ctx.HeaderInfo().Height, ctx.HeaderInfo().Time return &types.CheckpointStateUpdate{ State: status, BlockHeight: uint64(height), diff --git a/x/checkpointing/keeper/msg_server.go b/x/checkpointing/keeper/msg_server.go index 70baa36a5..04a088763 100644 --- a/x/checkpointing/keeper/msg_server.go +++ b/x/checkpointing/keeper/msg_server.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,20 +22,6 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} -// AddBlsSig adds BLS sig messages and changes a raw checkpoint status to SEALED if sufficient voting power is accumulated -func (m msgServer) AddBlsSig(goCtx context.Context, msg *types.MsgAddBlsSig) (*types.MsgAddBlsSigResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - ctx.Logger().Info(fmt.Sprintf("received BLS sig for epoch %d from %s", msg.BlsSig.EpochNum, msg.GetSigners())) - - err := m.k.addBlsSig(ctx, msg.BlsSig) - if err != nil { - return nil, err - } - - return &types.MsgAddBlsSigResponse{}, nil -} - // WrappedCreateValidator registers validator's BLS public key // and forwards corresponding MsgCreateValidator message to // the epoching module diff --git a/x/checkpointing/keeper/msg_server_test.go b/x/checkpointing/keeper/msg_server_test.go index cf68639f3..e8b32e471 100644 --- a/x/checkpointing/keeper/msg_server_test.go +++ b/x/checkpointing/keeper/msg_server_test.go @@ -16,9 +16,9 @@ import ( "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/privval" "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" checkpointingkeeper "github.com/babylonchain/babylon/x/checkpointing/keeper" "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/babylonchain/babylon/x/epoching/testepoching" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" ) @@ -32,18 +32,19 @@ func FuzzWrappedCreateValidator_InsufficientTokens(f *testing.F) { r := rand.New(rand.NewSource(seed)) // a genesis validator is generate for setup - helper := testepoching.NewHelper(t) - ek := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx := helper.Ctx + ek := helper.App.EpochingKeeper ck := helper.App.CheckpointingKeeper msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // epoch 1 right now epoch := ek.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) n := r.Intn(3) + 1 - addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(100000000)) + addrs, err := app.AddTestAddrs(helper.App, helper.Ctx, n, math.NewInt(100000000)) + require.NoError(t, err) // add n new validators with zero voting power via MsgWrappedCreateValidator wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) @@ -59,12 +60,10 @@ func FuzzWrappedCreateValidator_InsufficientTokens(f *testing.F) { } require.Len(t, ek.GetCurrentEpochMsgs(ctx), n) - // EndBlock of block 1 - ctx = helper.EndBlock() - - // go to BeginBlock of block 11, and thus entering epoch 2 + // go to block 11, and thus entering epoch 2 for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = ek.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) @@ -80,13 +79,14 @@ func FuzzWrappedCreateValidator_InsufficientTokens(f *testing.F) { // ensure all validators (not just validators in the val set) have correct bond status // - the 1st validator is bonded // - all the rest are unbonded since they have zero voting power - iterator := helper.StakingKeeper.ValidatorsPowerStoreIterator(ctx) + iterator, err := helper.App.StakingKeeper.ValidatorsPowerStoreIterator(ctx) + require.NoError(t, err) defer iterator.Close() count := 0 for ; iterator.Valid(); iterator.Next() { valAddr := sdk.ValAddress(iterator.Value()) - val, found := helper.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) + val, err := helper.App.StakingKeeper.GetValidator(ctx, valAddr) + require.NoError(t, err) count++ if count == 1 { require.Equal(t, stakingtypes.Bonded, val.Status) @@ -107,25 +107,26 @@ func FuzzWrappedCreateValidator_InsufficientBalance(f *testing.F) { r := rand.New(rand.NewSource(seed)) // a genesis validator is generate for setup - helper := testepoching.NewHelper(t) - ek := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx := helper.Ctx + ek := helper.App.EpochingKeeper ck := helper.App.CheckpointingKeeper msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // epoch 1 right now epoch := ek.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) n := r.Intn(3) + 1 balance := r.Int63n(100) - addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(balance)) + addrs, err := app.AddTestAddrs(helper.App, helper.Ctx, n, math.NewInt(balance)) + require.NoError(t, err) // add n new validators with value more than the delegator balance via MsgWrappedCreateValidator wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) for i := 0; i < n; i++ { // make sure the value is more than the balance - value := sdk.NewInt(balance).Add(sdk.NewInt(r.Int63n(100))) + value := math.NewInt(balance).Add(math.NewInt(r.Int63n(100))) msg, err := buildMsgWrappedCreateValidatorWithAmount(addrs[i], value) require.NoError(t, err) wcvMsgs[i] = msg @@ -147,19 +148,20 @@ func FuzzWrappedCreateValidator(f *testing.F) { r := rand.New(rand.NewSource(seed)) // a genesis validator is generate for setup - helper := testepoching.NewHelper(t) - ek := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx := helper.Ctx + ek := helper.App.EpochingKeeper ck := helper.App.CheckpointingKeeper msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // epoch 1 right now epoch := ek.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) // add n new validators via MsgWrappedCreateValidator n := r.Intn(3) - addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(100000000)) + addrs, err := app.AddTestAddrs(helper.App, helper.Ctx, n, math.NewInt(100000000)) + require.NoError(t, err) wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) for i := 0; i < n; i++ { @@ -174,12 +176,10 @@ func FuzzWrappedCreateValidator(f *testing.F) { } require.Len(t, ek.GetCurrentEpochMsgs(ctx), n) - // EndBlock of block 1 - ctx = helper.EndBlock() - - // go to BeginBlock of block 11, and thus entering epoch 2 + // go to block 11, and thus entering epoch 2 for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = ek.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) @@ -202,237 +202,30 @@ func FuzzWrappedCreateValidator(f *testing.F) { }) } -// FuzzAddBlsSig_NoError tests adding BLS signatures via MsgAddBlsSig -// it covers the following scenarios that would not cause errors: -// 1. a BLS signature is successfully accumulated and the checkpoint remains ACCUMULATING -// 2. a BLS signature is successfully accumulated and the checkpoint is changed to SEALED -// 3. a BLS signature is rejected if the checkpoint is not ACCUMULATING -func FuzzAddBlsSig_NoError(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 4) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - helper := testepoching.NewHelperWithValSet(t) - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper - msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) - - // apply 2 blocks to ensure that a raw checkpoint for the previous epoch is built - for i := uint64(0); i < 2; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - } - endingEpoch := ek.GetEpoch(ctx).EpochNumber - 1 - _, err := ck.GetRawCheckpoint(ctx, endingEpoch) - require.NoError(t, err) - - // add BLS signatures - n := len(helper.ValBlsPrivKeys) - totalPower := uint64(ck.GetTotalVotingPower(ctx, endingEpoch)) - for i := 0; i < n; i++ { - lch := ctx.BlockHeader().LastCommitHash - blsPrivKey := helper.ValBlsPrivKeys[i].BlsKey - addr := helper.ValBlsPrivKeys[i].Address - signBytes := types.GetSignBytes(endingEpoch, lch) - blsSig := bls12381.Sign(blsPrivKey, signBytes) - - // create MsgAddBlsSig message - msg := types.NewMsgAddBlsSig(sdk.AccAddress(addr), endingEpoch, lch, blsSig, addr) - _, err = msgServer.AddBlsSig(ctx, msg) - require.NoError(t, err) - afterCkpt, err := ck.GetRawCheckpoint(ctx, endingEpoch) - require.NoError(t, err) - if afterCkpt.PowerSum <= totalPower/3 { - require.True(t, afterCkpt.Status == types.Accumulating) - } else { - require.True(t, afterCkpt.Status == types.Sealed) - } - } - }) -} - -// FuzzAddBlsSig_Error tests adding BLS signatures via MsgAddBlsSig -// in a scenario where the signer is not in the checkpoint's validator set -func FuzzAddBlsSig_NotInValSet(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 4) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - helper := testepoching.NewHelperWithValSet(t) - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper - msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - // apply 2 blocks to ensure that a raw checkpoint for the previous epoch is built - for i := uint64(0); i < 2; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - } - endingEpoch := ek.GetEpoch(ctx).EpochNumber - 1 - _, err := ck.GetRawCheckpoint(ctx, endingEpoch) - require.NoError(t, err) - - // build BLS sig from a random validator (not in the validator set) - lch := ctx.BlockHeader().LastCommitHash - blsPrivKey := bls12381.GenPrivKey() - valAddr := datagen.GenRandomValidatorAddress() - signBytes := types.GetSignBytes(endingEpoch, lch) - blsSig := bls12381.Sign(blsPrivKey, signBytes) - msg := types.NewMsgAddBlsSig(sdk.AccAddress(valAddr), endingEpoch, lch, blsSig, valAddr) - - _, err = msgServer.AddBlsSig(ctx, msg) - require.Error(t, err, types.ErrCkptDoesNotExist) - }) -} - -// FuzzAddBlsSig_CkptNotExist tests adding BLS signatures via MsgAddBlsSig -// in a scenario where the corresponding checkpoint does not exist -func FuzzAddBlsSig_CkptNotExist(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 4) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - helper := testepoching.NewHelperWithValSet(t) - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper - msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) - - // build BLS signature from a random validator of the validator set - n := len(helper.ValBlsPrivKeys) - i := r.Intn(n) - lch := ctx.BlockHeader().LastCommitHash - blsPrivKey := helper.ValBlsPrivKeys[i].BlsKey - addr := helper.ValBlsPrivKeys[i].Address - signBytes := types.GetSignBytes(epoch.EpochNumber-1, lch) - blsSig := bls12381.Sign(blsPrivKey, signBytes) - msg := types.NewMsgAddBlsSig(sdk.AccAddress(addr), epoch.EpochNumber-1, lch, blsSig, addr) - - // add the BLS signature - _, err := msgServer.AddBlsSig(ctx, msg) - require.Error(t, err, types.ErrCkptDoesNotExist) - }) -} - -// FuzzAddBlsSig_WrongLastCommitHash tests adding BLS signatures via MsgAddBlsSig -// in a scenario where the signature is signed over wrong last_commit_hash -// 4. a BLS signature is rejected if the signature is invalid -func FuzzAddBlsSig_WrongLastCommitHash(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 4) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - helper := testepoching.NewHelperWithValSet(t) - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper - msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) - // apply 2 blocks to ensure that a raw checkpoint for the previous epoch is built - for i := uint64(0); i < 2; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - } - endingEpoch := ek.GetEpoch(ctx).EpochNumber - 1 - _, err := ck.GetRawCheckpoint(ctx, endingEpoch) - require.NoError(t, err) - - // build BLS sig from a random validator - n := len(helper.ValBlsPrivKeys) - i := r.Intn(n) - // inject random last commit hash - lch := datagen.GenRandomLastCommitHash(r) - blsPrivKey := helper.ValBlsPrivKeys[i].BlsKey - addr := helper.ValBlsPrivKeys[i].Address - signBytes := types.GetSignBytes(endingEpoch, lch) - blsSig := bls12381.Sign(blsPrivKey, signBytes) - msg := types.NewMsgAddBlsSig(sdk.AccAddress(addr), endingEpoch, lch, blsSig, addr) - - // add the BLS signature - _, err = msgServer.AddBlsSig(ctx, msg) - require.Error(t, err, types.ErrInvalidLastCommitHash) - }) -} - -// FuzzAddBlsSig_InvalidSignature tests adding BLS signatures via MsgAddBlsSig -// in a scenario where the signature is invalid -func FuzzAddBlsSig_InvalidSignature(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 4) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - helper := testepoching.NewHelperWithValSet(t) - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper - msgServer := checkpointingkeeper.NewMsgServerImpl(ck) - - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) - // apply 2 blocks to ensure that a raw checkpoint for the previous epoch is built - for i := uint64(0); i < 2; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - } - endingEpoch := ek.GetEpoch(ctx).EpochNumber - 1 - _, err := ck.GetRawCheckpoint(ctx, endingEpoch) - require.NoError(t, err) - - // build BLS sig from a random validator - n := len(helper.ValBlsPrivKeys) - i := r.Intn(n) - // inject random last commit hash - lch := ctx.BlockHeader().LastCommitHash - addr := helper.ValBlsPrivKeys[i].Address - blsSig := datagen.GenRandomBlsMultiSig(r) - msg := types.NewMsgAddBlsSig(sdk.AccAddress(addr), endingEpoch, lch, blsSig, addr) - - // add the BLS signature message - _, err = msgServer.AddBlsSig(ctx, msg) - require.Error(t, err, types.ErrInvalidBlsSignature) - }) -} - func buildMsgWrappedCreateValidator(addr sdk.AccAddress) (*types.MsgWrappedCreateValidator, error) { bondTokens := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) return buildMsgWrappedCreateValidatorWithAmount(addr, bondTokens) } func buildMsgWrappedCreateValidatorWithAmount(addr sdk.AccAddress, bondTokens math.Int) (*types.MsgWrappedCreateValidator, error) { - tmValPrivkey := ed25519.GenPrivKey() + cmtValPrivkey := ed25519.GenPrivKey() bondCoin := sdk.NewCoin(appparams.DefaultBondDenom, bondTokens) description := stakingtypes.NewDescription("foo_moniker", "", "", "", "") - commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) - pk, err := codec.FromTmPubKeyInterface(tmValPrivkey.PubKey()) + pk, err := codec.FromCmtPubKeyInterface(cmtValPrivkey.PubKey()) if err != nil { return nil, err } createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), pk, bondCoin, description, commission, sdk.OneInt(), + sdk.ValAddress(addr).String(), pk, bondCoin, description, commission, math.OneInt(), ) if err != nil { return nil, err } blsPrivKey := bls12381.GenPrivKey() - pop, err := privval.BuildPoP(tmValPrivkey, blsPrivKey) + pop, err := privval.BuildPoP(cmtValPrivkey, blsPrivKey) if err != nil { return nil, err } diff --git a/x/checkpointing/keeper/querier.go b/x/checkpointing/keeper/querier.go deleted file mode 100644 index b55569d4a..000000000 --- a/x/checkpointing/keeper/querier.go +++ /dev/null @@ -1 +0,0 @@ -package keeper diff --git a/x/checkpointing/keeper/registration_state.go b/x/checkpointing/keeper/registration_state.go index fe6038f27..4649439e7 100644 --- a/x/checkpointing/keeper/registration_state.go +++ b/x/checkpointing/keeper/registration_state.go @@ -1,28 +1,31 @@ package keeper import ( + "context" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/x/checkpointing/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) type RegistrationState struct { cdc codec.BinaryCodec // addrToBlsKeys maps validator addresses to BLS public keys - addrToBlsKeys sdk.KVStore + addrToBlsKeys storetypes.KVStore // blsKeysToAddr maps BLS public keys to validator addresses - blsKeysToAddr sdk.KVStore + blsKeysToAddr storetypes.KVStore } -func (k Keeper) RegistrationState(ctx sdk.Context) RegistrationState { +func (k Keeper) RegistrationState(ctx context.Context) RegistrationState { // Build the RegistrationState storage - store := ctx.KVStore(k.storeKey) + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) return RegistrationState{ cdc: k.cdc, - addrToBlsKeys: prefix.NewStore(store, types.AddrToBlsKeyPrefix), - blsKeysToAddr: prefix.NewStore(store, types.BlsKeyToAddrPrefix), + addrToBlsKeys: prefix.NewStore(storeAdapter, types.AddrToBlsKeyPrefix), + blsKeysToAddr: prefix.NewStore(storeAdapter, types.BlsKeyToAddrPrefix), } } diff --git a/x/checkpointing/keeper/val_bls_set.go b/x/checkpointing/keeper/val_bls_set.go index 001d60410..a3ba8df88 100644 --- a/x/checkpointing/keeper/val_bls_set.go +++ b/x/checkpointing/keeper/val_bls_set.go @@ -1,16 +1,18 @@ package keeper import ( + "context" + "cosmossdk.io/store/prefix" "fmt" "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) // GetValidatorBlsKeySet returns the set of validators of a given epoch with BLS public key // the validators are ordered by their address in ascending order -func (k Keeper) GetValidatorBlsKeySet(ctx sdk.Context, epochNumber uint64) *types.ValidatorWithBlsKeySet { - store := k.valBlsSetStore(ctx, epochNumber) +func (k Keeper) GetValidatorBlsKeySet(ctx context.Context, epochNumber uint64) *types.ValidatorWithBlsKeySet { + store := k.valBlsSetStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) valBlsKeySetBytes := store.Get(epochNumberBytes) valBlsKeySet, err := types.BytesToValidatorBlsKeySet(k.cdc, valBlsKeySetBytes) @@ -20,14 +22,14 @@ func (k Keeper) GetValidatorBlsKeySet(ctx sdk.Context, epochNumber uint64) *type return valBlsKeySet } -func (k Keeper) GetCurrentValidatorBlsKeySet(ctx sdk.Context) *types.ValidatorWithBlsKeySet { +func (k Keeper) GetCurrentValidatorBlsKeySet(ctx context.Context) *types.ValidatorWithBlsKeySet { epochNumber := k.GetEpoch(ctx).EpochNumber return k.GetValidatorBlsKeySet(ctx, epochNumber) } // InitValidatorBLSSet stores the validator set with BLS keys in the beginning of the current epoch // This is called upon BeginBlock -func (k Keeper) InitValidatorBLSSet(ctx sdk.Context) error { +func (k Keeper) InitValidatorBLSSet(ctx context.Context) error { epochNumber := k.GetEpoch(ctx).EpochNumber valset := k.GetValidatorSet(ctx, epochNumber) valBlsSet := &types.ValidatorWithBlsKeySet{ @@ -46,7 +48,7 @@ func (k Keeper) InitValidatorBLSSet(ctx sdk.Context) error { valBlsSet.ValSet[i] = valBls } valBlsSetBytes := types.ValidatorBlsKeySetToBytes(k.cdc, valBlsSet) - store := k.valBlsSetStore(ctx, epochNumber) + store := k.valBlsSetStore(ctx) store.Set(types.ValidatorBlsKeySetKey(epochNumber), valBlsSetBytes) return nil @@ -54,8 +56,8 @@ func (k Keeper) InitValidatorBLSSet(ctx sdk.Context) error { // ClearValidatorSet removes the validator BLS set of a given epoch // TODO: This is called upon the epoch is checkpointed -func (k Keeper) ClearValidatorSet(ctx sdk.Context, epochNumber uint64) { - store := k.valBlsSetStore(ctx, epochNumber) +func (k Keeper) ClearValidatorSet(ctx context.Context, epochNumber uint64) { + store := k.valBlsSetStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) store.Delete(epochNumberBytes) } @@ -64,7 +66,7 @@ func (k Keeper) ClearValidatorSet(ctx sdk.Context, epochNumber uint64) { // prefix: ValidatorBLSSetKey // key: epoch number // value: ValidatorBLSKeySet -func (k Keeper) valBlsSetStore(ctx sdk.Context, epochNumber uint64) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.ValidatorBlsKeySetPrefix) +func (k Keeper) valBlsSetStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.ValidatorBlsKeySetPrefix) } diff --git a/x/checkpointing/keeper/val_bls_set_test.go b/x/checkpointing/keeper/val_bls_set_test.go index 13aaa7939..1c4e84292 100644 --- a/x/checkpointing/keeper/val_bls_set_test.go +++ b/x/checkpointing/keeper/val_bls_set_test.go @@ -4,14 +4,16 @@ import ( "math/rand" "testing" + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/stretchr/testify/require" + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" checkpointingkeeper "github.com/babylonchain/babylon/x/checkpointing/keeper" "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" ) func FuzzGetValidatorBlsKeySet(f *testing.F) { @@ -19,8 +21,9 @@ func FuzzGetValidatorBlsKeySet(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) // a genesis validator is generated for setup - helper := testepoching.NewHelper(t) - ek := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx := helper.Ctx + ek := helper.App.EpochingKeeper ck := helper.App.CheckpointingKeeper queryHelper := baseapp.NewQueryServerTestHelper(helper.Ctx, helper.App.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, ck) @@ -29,8 +32,7 @@ func FuzzGetValidatorBlsKeySet(f *testing.F) { genesisBLSPubkey, err := ck.GetBlsPubKey(helper.Ctx, genesisVal.Addr) require.NoError(t, err) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // epoch 1 right now epoch := ek.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) @@ -42,7 +44,8 @@ func FuzzGetValidatorBlsKeySet(f *testing.F) { // add n new validators via MsgWrappedCreateValidator n := r.Intn(10) + 1 - addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(100000000)) + addrs, err := app.AddTestAddrs(helper.App, helper.Ctx, n, math.NewInt(100000000)) + require.NoError(t, err) wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) for i := 0; i < n; i++ { @@ -53,12 +56,10 @@ func FuzzGetValidatorBlsKeySet(f *testing.F) { require.NoError(t, err) } - // EndBlock of block 1 - ctx = helper.EndBlock() - - // go to BeginBlock of block 11, and thus entering epoch 2 + // go to block 11, and thus entering epoch 2 for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = ek.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) diff --git a/x/checkpointing/module.go b/x/checkpointing/module.go index 3e9db023e..5e4bf3184 100644 --- a/x/checkpointing/module.go +++ b/x/checkpointing/module.go @@ -2,6 +2,7 @@ package checkpointing import ( "context" + "cosmossdk.io/core/appmodule" "encoding/json" "fmt" @@ -22,8 +23,10 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // ---------------------------------------------------------------------------- @@ -98,23 +101,16 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { type AppModule struct { AppModuleBasic - keeper keeper.Keeper - accountKeeper types.AccountKeeper - bankKeeper types.BankKeeper - // TODO: add dependencies to staking, slashing and evidence + keeper keeper.Keeper } func NewAppModule( cdc codec.Codec, keeper keeper.Keeper, - accountKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, ) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), keeper: keeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, } } @@ -138,14 +134,13 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the capability module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. @@ -158,12 +153,20 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper, req) +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) } // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. -func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - return []abci.ValidatorUpdate{} +func (am AppModule) EndBlock(_ context.Context) ([]abci.ValidatorUpdate, error) { + return []abci.ValidatorUpdate{}, nil +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker } diff --git a/x/checkpointing/module_simulation.go b/x/checkpointing/module_simulation.go deleted file mode 100644 index db476aa0d..000000000 --- a/x/checkpointing/module_simulation.go +++ /dev/null @@ -1,49 +0,0 @@ -package checkpointing - -import ( - simappparams "github.com/babylonchain/babylon/app/params" - "github.com/babylonchain/babylon/testutil/sample" - checkpointingsimulation "github.com/babylonchain/babylon/x/checkpointing/simulation" - "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = sample.AccAddress - _ = checkpointingsimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -const () - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - accs := make([]string, len(simState.Accounts)) - for i, acc := range simState.Accounts { - accs[i] = acc.Address.String() - } - headeroracleGenesis := types.GenesisState{} - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&headeroracleGenesis) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - operations := make([]simtypes.WeightedOperation, 0) - - return operations -} diff --git a/x/checkpointing/proposal.go b/x/checkpointing/proposal.go new file mode 100644 index 000000000..bb0b3200a --- /dev/null +++ b/x/checkpointing/proposal.go @@ -0,0 +1,329 @@ +package checkpointing + +import ( + "fmt" + "slices" + + "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/mempool" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/babylonchain/babylon/x/checkpointing/keeper" + ckpttypes "github.com/babylonchain/babylon/x/checkpointing/types" +) + +const defaultInjectedTxIndex = 0 + +type ProposalHandler struct { + logger log.Logger + ckptKeeper *keeper.Keeper + valStore baseapp.ValidatorStore + txVerifier baseapp.ProposalTxVerifier + defaultPrepareProposalHandler sdk.PrepareProposalHandler + defaultProcessProposalHandler sdk.ProcessProposalHandler +} + +func NewProposalHandler(logger log.Logger, ckptKeeper *keeper.Keeper, mp mempool.Mempool, txVerifier baseapp.ProposalTxVerifier) *ProposalHandler { + defaultHandler := baseapp.NewDefaultProposalHandler(mp, txVerifier) + return &ProposalHandler{ + logger: logger, + ckptKeeper: ckptKeeper, + valStore: ckptKeeper, + txVerifier: txVerifier, + defaultPrepareProposalHandler: defaultHandler.PrepareProposalHandler(), + defaultProcessProposalHandler: defaultHandler.ProcessProposalHandler(), + } +} + +func (h *ProposalHandler) SetHandlers(bApp *baseapp.BaseApp) { + bApp.SetPrepareProposal(h.PrepareProposal()) + bApp.SetProcessProposal(h.ProcessProposal()) + bApp.SetPreBlocker(h.PreBlocker()) +} + +// PrepareProposal examines the vote extensions from the previous block, accumulates +// them into a checkpoint, and injects the checkpoint into the current proposal +// as a special tx +// Warning: the returned error of the handler will cause panic of the proposer, +// therefore we only return error when something really wrong happened +func (h *ProposalHandler) PrepareProposal() sdk.PrepareProposalHandler { + return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { + // call default handler first to do basic validation + res, err := h.defaultPrepareProposalHandler(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed in default PrepareProposal handler: %w", err) + } + + k := h.ckptKeeper + proposalTxs := res.Txs + proposalRes := &abci.ResponsePrepareProposal{Txs: proposalTxs} + + epoch := k.GetEpoch(ctx) + // BLS signatures are sent in the last block of the previous epoch, + // so they should be aggregated in the first block of the new epoch + // and no BLS signatures are send in epoch 0 + if !epoch.IsVoteExtensionProposal(ctx) { + return proposalRes, nil + } + + if len(req.LocalLastCommit.Votes) == 0 { + return proposalRes, fmt.Errorf("no extended votes received from the last block") + } + + // 1. verify the validity of vote extensions (2/3 majority is achieved) + err = baseapp.ValidateVoteExtensions(ctx, h.valStore, req.Height, ctx.ChainID(), req.LocalLastCommit) + if err != nil { + return proposalRes, fmt.Errorf("invalid vote extensions: %w", err) + } + + // 2. build a checkpoint for the previous epoch + // Note: the epoch has not increased yet, so + // we can use the current epoch + ckpt, err := h.buildCheckpointFromVoteExtensions(ctx, epoch.EpochNumber, req.LocalLastCommit.Votes) + if err != nil { + return proposalRes, fmt.Errorf("failed to build checkpoint from vote extensions: %w", err) + } + + // 3. inject a "fake" tx into the proposal s.t. validators can decode, verify the checkpoint + injectedCkpt := &ckpttypes.InjectedCheckpoint{ + Ckpt: ckpt, + ExtendedCommitInfo: &req.LocalLastCommit, + } + injectedVoteExtTx, err := injectedCkpt.Marshal() + if err != nil { + return nil, fmt.Errorf("failed to encode vote extensions into a special tx: %w", err) + } + proposalTxs = slices.Insert(proposalTxs, defaultInjectedTxIndex, [][]byte{injectedVoteExtTx}...) + + return &abci.ResponsePrepareProposal{ + Txs: proposalTxs, + }, nil + } +} + +func (h *ProposalHandler) buildCheckpointFromVoteExtensions(ctx sdk.Context, epoch uint64, extendedVotes []abci.ExtendedVoteInfo) (*ckpttypes.RawCheckpointWithMeta, error) { + prevBlockID, err := h.findLastBlockHash(extendedVotes) + if err != nil { + return nil, err + } + ckpt := ckpttypes.NewCheckpointWithMeta(ckpttypes.NewCheckpoint(epoch, prevBlockID), ckpttypes.Accumulating) + validBLSSigs := h.getValidBlsSigs(ctx, extendedVotes) + vals := h.ckptKeeper.GetValidatorSet(ctx, epoch) + totalPower := h.ckptKeeper.GetTotalVotingPower(ctx, epoch) + // TODO: maybe we don't need to verify BLS sigs anymore as they are already + // verified by VerifyVoteExtension + for _, sig := range validBLSSigs { + signerAddress, err := sdk.ValAddressFromBech32(sig.SignerAddress) + if err != nil { + h.logger.Error( + "skip invalid BLS sig", + "invalid signer address", sig.SignerAddress, + "err", err, + ) + continue + } + signerBlsKey, err := h.ckptKeeper.GetBlsPubKey(ctx, signerAddress) + if err != nil { + h.logger.Error( + "skip invalid BLS sig", + "can't find BLS public key", err, + ) + continue + } + err = ckpt.Accumulate(vals, signerAddress, signerBlsKey, *sig.BlsSig, totalPower) + if err != nil { + h.logger.Error( + "skip invalid BLS sig", + "accumulation failed", err, + ) + continue + } + // sufficient voting power is accumulated + if ckpt.Status == ckpttypes.Sealed { + break + } + } + if ckpt.Status != ckpttypes.Sealed { + return nil, fmt.Errorf("insufficient voting power to build the checkpoint") + } + + return ckpt, nil +} + +func (h *ProposalHandler) getValidBlsSigs(ctx sdk.Context, extendedVotes []abci.ExtendedVoteInfo) []ckpttypes.BlsSig { + k := h.ckptKeeper + validBLSSigs := make([]ckpttypes.BlsSig, 0, len(extendedVotes)) + for _, voteInfo := range extendedVotes { + veBytes := voteInfo.VoteExtension + if len(veBytes) == 0 { + h.logger.Error("received empty vote extension", "validator", voteInfo.Validator.String()) + continue + } + var ve ckpttypes.VoteExtension + if err := ve.Unmarshal(veBytes); err != nil { + h.logger.Error("failed to unmarshal vote extension", "err", err) + continue + } + sig := ve.ToBLSSig() + + if err := k.VerifyBLSSig(ctx, sig); err != nil { + h.logger.Error("invalid BLS signature", "err", err) + continue + } + + validBLSSigs = append(validBLSSigs, *sig) + } + + return validBLSSigs +} + +// findLastBlockHash finds the last block hash from the first vote extension +// this is a workaround that the last block hash can't be obtained from the context +// this is also safe as the BLS sig is already verified by VerifyVoteExtension +func (h *ProposalHandler) findLastBlockHash(extendedVotes []abci.ExtendedVoteInfo) ([]byte, error) { + var ve ckpttypes.VoteExtension + if err := ve.Unmarshal(extendedVotes[0].VoteExtension); err != nil { + return nil, err + } + return ve.ToBLSSig().BlockHash.MustMarshal(), nil +} + +// ProcessProposal examines the checkpoint in the injected tx of the proposal +// Warning: the returned error of the handler will cause panic of the node, +// therefore we only return error when something really wrong happened +func (h *ProposalHandler) ProcessProposal() sdk.ProcessProposalHandler { + return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { + resAccept := &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} + resReject := &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} + + k := h.ckptKeeper + + epoch := k.GetEpoch(ctx) + // BLS signatures are sent in the last block of the previous epoch, + // so they should be aggregated in the first block of the new epoch + // and no BLS signatures are send in epoch 0 + if epoch.IsVoteExtensionProposal(ctx) { + // 1. extract the special tx containing the checkpoint + injectedCkpt, err := extractInjectedCheckpoint(req.Txs) + if err != nil { + h.logger.Error("cannot get injected checkpoint", "err", err) + // should not return error here as error will cause panic + return resReject, nil + } + + // 2. remove the special tx from the request so that + // the rest of the txs can be handled by the default handler + req.Txs, err = removeInjectedTx(req.Txs) + if err != nil { + // should not return error here as error will cause panic + h.logger.Error("failed to remove injected tx from request: %w", err) + return resReject, nil + } + + // 3. verify the validity of the vote extension (2/3 majority is achieved) + err = baseapp.ValidateVoteExtensions(ctx, h.valStore, req.Height, ctx.ChainID(), *injectedCkpt.ExtendedCommitInfo) + if err != nil { + // the returned err will lead to panic as something very wrong happened during consensus + return resReject, err + } + + // 4. rebuild the checkpoint from vote extensions and compare it with + // the injected checkpoint + // Note: this is needed because LastBlockID is not available here so that + // we can't verify whether the injected checkpoint is signing the correct + // LastBlockID + ckpt, err := h.buildCheckpointFromVoteExtensions(ctx, epoch.EpochNumber, injectedCkpt.ExtendedCommitInfo.Votes) + if err != nil { + // should not return error here as error will cause panic + h.logger.Error("invalid vote extensions: %w", err) + return resReject, nil + } + // TODO it is possible that although the checkpoints do not match but the injected + // checkpoint is still valid. This indicates the existence of a fork (>1/3 malicious voting power) + // and we should probably send an alarm and stall the blockchain + if !ckpt.Equal(injectedCkpt.Ckpt) { + // should not return error here as error will cause panic + h.logger.Error("invalid checkpoint in vote extension tx", "err", err) + return resReject, nil + } + } + + // 5. verify the rest of the txs using the default handler + res, err := h.defaultProcessProposalHandler(ctx, req) + if err != nil { + return resReject, fmt.Errorf("failed in default ProcessProposal handler: %w", err) + } + if !res.IsAccepted() { + h.logger.Error("the proposal is rejected by default ProcessProposal handler", + "height", req.Height, "epoch", epoch.EpochNumber) + return resReject, nil + } + + return resAccept, nil + } +} + +// PreBlocker extracts the checkpoint from the injected tx and stores it in +// the application +// no more validation is needed as it is already done in ProcessProposal +func (h *ProposalHandler) PreBlocker() sdk.PreBlocker { + return func(ctx sdk.Context, req *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) { + k := h.ckptKeeper + res := &sdk.ResponsePreBlock{} + + epoch := k.GetEpoch(ctx) + // BLS signatures are sent in the last block of the previous epoch, + // so they should be aggregated in the first block of the new epoch + // and no BLS signatures are send in epoch 0 + if !epoch.IsVoteExtensionProposal(ctx) { + return res, nil + } + + // 1. extract the special tx containing BLS sigs + injectedCkpt, err := extractInjectedCheckpoint(req.Txs) + if err != nil { + return res, fmt.Errorf("failed to get extract injected checkpoint from the tx set: %w", err) + } + + // 2. update checkpoint + if err := k.SealCheckpoint(ctx, injectedCkpt.Ckpt); err != nil { + return res, fmt.Errorf("failed to update checkpoint: %w", err) + } + + return res, nil + } +} + +// extractInjectedCheckpoint extracts the injected checkpoint from the tx set +func extractInjectedCheckpoint(txs [][]byte) (*ckpttypes.InjectedCheckpoint, error) { + if len(txs) < defaultInjectedTxIndex+1 { + return nil, fmt.Errorf("the tx set does not contain the injected tx") + } + + injectedTx := txs[defaultInjectedTxIndex] + + if len(injectedTx) == 0 { + return nil, fmt.Errorf("err in PreBlocker: the injected vote extensions tx is empty") + } + + var injectedCkpt ckpttypes.InjectedCheckpoint + if err := injectedCkpt.Unmarshal(injectedTx); err != nil { + return nil, fmt.Errorf("failed to decode injected vote extension tx: %w", err) + } + + return &injectedCkpt, nil +} + +// removeInjectedTx removes the injected tx from the tx set +func removeInjectedTx(txs [][]byte) ([][]byte, error) { + if len(txs) < defaultInjectedTxIndex+1 { + return nil, fmt.Errorf("the tx set does not contain the injected tx") + } + + txs = append(txs[:defaultInjectedTxIndex], txs[defaultInjectedTxIndex+1:]...) + + return txs, nil +} diff --git a/x/checkpointing/simulation/genesis.go b/x/checkpointing/simulation/genesis.go deleted file mode 100644 index 02eddda2b..000000000 --- a/x/checkpointing/simulation/genesis.go +++ /dev/null @@ -1 +0,0 @@ -package simulation diff --git a/x/checkpointing/simulation/operations.go b/x/checkpointing/simulation/operations.go deleted file mode 100644 index 02eddda2b..000000000 --- a/x/checkpointing/simulation/operations.go +++ /dev/null @@ -1 +0,0 @@ -package simulation diff --git a/x/checkpointing/simulation/simap.go b/x/checkpointing/simulation/simap.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/checkpointing/simulation/simap.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/checkpointing/testckpt/helper.go b/x/checkpointing/testckpt/helper.go deleted file mode 100644 index 9f57a1311..000000000 --- a/x/checkpointing/testckpt/helper.go +++ /dev/null @@ -1,109 +0,0 @@ -package testckpt - -import ( - "testing" - - "cosmossdk.io/math" - "github.com/babylonchain/babylon/app" - appparams "github.com/babylonchain/babylon/app/params" - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/testutil/datagen" - "github.com/babylonchain/babylon/x/checkpointing/keeper" - "github.com/babylonchain/babylon/x/checkpointing/types" - epochingkeeper "github.com/babylonchain/babylon/x/epoching/keeper" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/baseapp" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - proto "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/require" -) - -// Helper is a structure which wraps the entire app and exposes functionalities for testing the epoching module -type Helper struct { - t *testing.T - - Ctx sdk.Context - App *app.BabylonApp - CheckpointingKeeper *keeper.Keeper - MsgSrvr types.MsgServer - QueryClient types.QueryClient - StakingKeeper *stakingkeeper.Keeper - EpochingKeeper *epochingkeeper.Keeper - - GenAccs []authtypes.GenesisAccount -} - -// NewHelper creates the helper for testing the epoching module -func NewHelper(t *testing.T, n int) *Helper { - accs, balances := datagen.GenRandomAccWithBalance(n) - app := app.SetupWithGenesisAccounts(accs, balances...) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - checkpointingKeeper := app.CheckpointingKeeper - epochingKeeper := app.EpochingKeeper - stakingKeeper := app.StakingKeeper - queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, checkpointingKeeper) - queryClient := types.NewQueryClient(queryHelper) - msgSrvr := keeper.NewMsgServerImpl(checkpointingKeeper) - - return &Helper{ - t: t, - Ctx: ctx, - App: app, - CheckpointingKeeper: &checkpointingKeeper, - MsgSrvr: msgSrvr, - QueryClient: queryClient, - StakingKeeper: stakingKeeper, - EpochingKeeper: &epochingKeeper, - GenAccs: accs, - } -} - -// CreateValidator calls handler to create a new staking validator -func (h *Helper) CreateValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, blsPK *bls12381.PublicKey, pop *types.ProofOfPossession, stakeAmount math.Int, ok bool) { - coin := sdk.NewCoin(appparams.DefaultBondDenom, stakeAmount) - h.createValidator(addr, pk, blsPK, pop, coin, ok) -} - -// CreateValidatorWithValPower calls handler to create a new staking validator with zero commission -func (h *Helper) CreateValidatorWithValPower(addr sdk.ValAddress, pk cryptotypes.PubKey, blsPK *bls12381.PublicKey, pop *types.ProofOfPossession, valPower int64, ok bool) math.Int { - amount := h.StakingKeeper.TokensFromConsensusPower(h.Ctx, valPower) - coin := sdk.NewCoin(appparams.DefaultBondDenom, amount) - h.createValidator(addr, pk, blsPK, pop, coin, ok) - return amount -} - -// CreateValidatorMsg returns a message used to create validator in this service. -func (h *Helper) CreateValidatorMsg(addr sdk.ValAddress, pk cryptotypes.PubKey, blsPK *bls12381.PublicKey, pop *types.ProofOfPossession, stakeAmount math.Int) *types.MsgWrappedCreateValidator { - coin := sdk.NewCoin(appparams.DefaultBondDenom, stakeAmount) - msg, err := stakingtypes.NewMsgCreateValidator(addr, pk, coin, stakingtypes.Description{}, ZeroCommission(), sdk.OneInt()) - require.NoError(h.t, err) - wmsg, err := types.NewMsgWrappedCreateValidator(msg, blsPK, pop) - require.NoError(h.t, err) - return wmsg -} - -func (h *Helper) createValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, blsPK *bls12381.PublicKey, pop *types.ProofOfPossession, coin sdk.Coin, ok bool) { - h.Handle(func(ctx sdk.Context) (proto.Message, error) { - return h.CreateValidatorMsg(addr, pk, blsPK, pop, coin.Amount), nil - }) -} - -// Handle executes an action function with the Helper's context, wraps the result into an SDK service result, and performs two assertions before returning it -func (h *Helper) Handle(action func(sdk.Context) (proto.Message, error)) *sdk.Result { - res, err := action(h.Ctx) - r, _ := sdk.WrapServiceResult(h.Ctx, res, err) - require.NotNil(h.t, r) - require.NoError(h.t, err) - return r -} - -// ZeroCommission constructs a commission rates with all zeros. -func ZeroCommission() stakingtypes.CommissionRates { - return stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) -} diff --git a/x/checkpointing/types/bls_key.pb.go b/x/checkpointing/types/bls_key.pb.go index d42efd40d..dd52170dc 100644 --- a/x/checkpointing/types/bls_key.pb.go +++ b/x/checkpointing/types/bls_key.pb.go @@ -27,9 +27,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // BlsKey wraps BLS public key with PoP type BlsKey struct { // pubkey is the BLS public key of a validator - Pubkey *github_com_babylonchain_babylon_crypto_bls12381.PublicKey `protobuf:"bytes,2,opt,name=pubkey,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.PublicKey" json:"pubkey,omitempty"` + Pubkey *github_com_babylonchain_babylon_crypto_bls12381.PublicKey `protobuf:"bytes,1,opt,name=pubkey,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.PublicKey" json:"pubkey,omitempty"` // pop is the proof-of-possession of the BLS key - Pop *ProofOfPossession `protobuf:"bytes,3,opt,name=pop,proto3" json:"pop,omitempty"` + Pop *ProofOfPossession `protobuf:"bytes,2,opt,name=pop,proto3" json:"pop,omitempty"` } func (m *BlsKey) Reset() { *m = BlsKey{} } @@ -77,10 +77,10 @@ func (m *BlsKey) GetPop() *ProofOfPossession { type ProofOfPossession struct { // ed25519_sig is used for verification, ed25519_sig = sign(key = Ed25519_sk, // data = BLS_pk) - Ed25519Sig []byte `protobuf:"bytes,2,opt,name=ed25519_sig,json=ed25519Sig,proto3" json:"ed25519_sig,omitempty"` + Ed25519Sig []byte `protobuf:"bytes,1,opt,name=ed25519_sig,json=ed25519Sig,proto3" json:"ed25519_sig,omitempty"` // bls_sig is the result of PoP, bls_sig = sign(key = BLS_sk, data = // ed25519_sig) - BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,3,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` + BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,2,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` } func (m *ProofOfPossession) Reset() { *m = ProofOfPossession{} } @@ -171,9 +171,12 @@ func (m *ValidatorWithBlsKeySet) GetValSet() []*ValidatorWithBlsKey { // ValidatorWithBlsKey couples validator address, voting power, and its bls // public key type ValidatorWithBlsKey struct { + // validator_address is the address of the validator ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` - BlsPubKey []byte `protobuf:"bytes,2,opt,name=bls_pub_key,json=blsPubKey,proto3" json:"bls_pub_key,omitempty"` - VotingPower uint64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` + // bls_pub_key is the BLS public key of the validator + BlsPubKey []byte `protobuf:"bytes,2,opt,name=bls_pub_key,json=blsPubKey,proto3" json:"bls_pub_key,omitempty"` + // voting_power is the voting power of the validator at the given epoch + VotingPower uint64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` } func (m *ValidatorWithBlsKey) Reset() { *m = ValidatorWithBlsKey{} } @@ -230,11 +233,96 @@ func (m *ValidatorWithBlsKey) GetVotingPower() uint64 { return 0 } +// VoteExtension defines the structure used to create a BLS vote extension. +type VoteExtension struct { + // signer is the address of the vote extension signer + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // validator_address is the address of the validator + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` + // block_hash is the hash of the block that the vote extension is signed over + BlockHash []byte `protobuf:"bytes,3,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + // epoch_num is the epoch number of the vote extension + EpochNum uint64 `protobuf:"varint,4,opt,name=epoch_num,json=epochNum,proto3" json:"epoch_num,omitempty"` + // height is the height of the vote extension + Height uint64 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"` + // bls_sig is the BLS signature + BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,6,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` +} + +func (m *VoteExtension) Reset() { *m = VoteExtension{} } +func (m *VoteExtension) String() string { return proto.CompactTextString(m) } +func (*VoteExtension) ProtoMessage() {} +func (*VoteExtension) Descriptor() ([]byte, []int) { + return fileDescriptor_3a8c0d37ce63f038, []int{4} +} +func (m *VoteExtension) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VoteExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VoteExtension.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 *VoteExtension) XXX_Merge(src proto.Message) { + xxx_messageInfo_VoteExtension.Merge(m, src) +} +func (m *VoteExtension) XXX_Size() int { + return m.Size() +} +func (m *VoteExtension) XXX_DiscardUnknown() { + xxx_messageInfo_VoteExtension.DiscardUnknown(m) +} + +var xxx_messageInfo_VoteExtension proto.InternalMessageInfo + +func (m *VoteExtension) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *VoteExtension) GetValidatorAddress() string { + if m != nil { + return m.ValidatorAddress + } + return "" +} + +func (m *VoteExtension) GetBlockHash() []byte { + if m != nil { + return m.BlockHash + } + return nil +} + +func (m *VoteExtension) GetEpochNum() uint64 { + if m != nil { + return m.EpochNum + } + return 0 +} + +func (m *VoteExtension) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + func init() { proto.RegisterType((*BlsKey)(nil), "babylon.checkpointing.v1.BlsKey") proto.RegisterType((*ProofOfPossession)(nil), "babylon.checkpointing.v1.ProofOfPossession") proto.RegisterType((*ValidatorWithBlsKeySet)(nil), "babylon.checkpointing.v1.ValidatorWithBlsKeySet") proto.RegisterType((*ValidatorWithBlsKey)(nil), "babylon.checkpointing.v1.ValidatorWithBlsKey") + proto.RegisterType((*VoteExtension)(nil), "babylon.checkpointing.v1.VoteExtension") } func init() { @@ -242,35 +330,40 @@ func init() { } var fileDescriptor_3a8c0d37ce63f038 = []byte{ - // 433 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x41, 0x6b, 0xd4, 0x40, - 0x18, 0xdd, 0x71, 0x25, 0xa5, 0x93, 0x1e, 0x6c, 0x14, 0x09, 0x1e, 0xd2, 0x75, 0x0f, 0xb2, 0x50, - 0x4c, 0xc8, 0x96, 0x05, 0x7b, 0xe8, 0xc1, 0x3d, 0x78, 0xe9, 0xa1, 0x21, 0xc1, 0x0a, 0x5e, 0xe2, - 0x4c, 0x76, 0x3a, 0x3b, 0xec, 0x98, 0x6f, 0xc8, 0x4c, 0xa2, 0xf9, 0x01, 0xde, 0x3c, 0xf8, 0x0b, - 0xfc, 0x3d, 0x1e, 0x7b, 0x14, 0x0f, 0x22, 0xbb, 0x7f, 0x44, 0x66, 0x13, 0x17, 0xd4, 0x16, 0xc1, - 0x5b, 0x78, 0xef, 0xe5, 0xf1, 0xde, 0x9b, 0x0f, 0x3f, 0xa1, 0x84, 0xb6, 0x12, 0xca, 0xa8, 0x58, - 0xb2, 0x62, 0xa5, 0x40, 0x94, 0x46, 0x94, 0x3c, 0x6a, 0xe2, 0x88, 0x4a, 0x9d, 0xaf, 0x58, 0x1b, - 0xaa, 0x0a, 0x0c, 0x78, 0x7e, 0xaf, 0x0b, 0x7f, 0xd3, 0x85, 0x4d, 0xfc, 0xe8, 0x01, 0x07, 0x0e, - 0x5b, 0x51, 0x64, 0xbf, 0x3a, 0xfd, 0xf8, 0x33, 0xc2, 0xce, 0x5c, 0xea, 0x73, 0xd6, 0x7a, 0x2f, - 0xb1, 0xa3, 0x6a, 0xba, 0x62, 0xad, 0x7f, 0x67, 0x84, 0x26, 0x07, 0xf3, 0xb3, 0x6f, 0xdf, 0x8f, - 0x4e, 0xb9, 0x30, 0xcb, 0x9a, 0x86, 0x05, 0xbc, 0x8d, 0x7a, 0xe7, 0x62, 0x49, 0x44, 0x19, 0xed, - 0xe2, 0x54, 0xad, 0x32, 0x60, 0x43, 0xc4, 0xd3, 0x93, 0x67, 0x71, 0x98, 0xd4, 0x54, 0x8a, 0xe2, - 0x9c, 0xb5, 0x69, 0x6f, 0xe6, 0x9d, 0xe1, 0xa1, 0x02, 0xe5, 0x0f, 0x47, 0x68, 0xe2, 0x4e, 0x8f, - 0xc3, 0xdb, 0xf2, 0x85, 0x49, 0x05, 0x70, 0x75, 0x71, 0x95, 0x80, 0xd6, 0x4c, 0x6b, 0x01, 0x65, - 0x6a, 0xff, 0x1b, 0x7f, 0x44, 0xf8, 0xf0, 0x2f, 0xca, 0x3b, 0xc2, 0x2e, 0x5b, 0x4c, 0x67, 0xb3, - 0xf8, 0x34, 0xd7, 0x82, 0x77, 0x81, 0x53, 0xdc, 0x43, 0x99, 0xe0, 0xde, 0x25, 0xde, 0xb3, 0xc3, - 0x58, 0x72, 0xf8, 0xff, 0x6d, 0x32, 0xc1, 0x4b, 0x62, 0xea, 0x8a, 0xa5, 0x0e, 0x95, 0x3a, 0x13, - 0x7c, 0xfc, 0x06, 0x3f, 0xbc, 0x24, 0x52, 0x2c, 0x88, 0x81, 0xea, 0x95, 0x30, 0xcb, 0x6e, 0xbb, - 0x8c, 0x19, 0xef, 0x05, 0xde, 0x6b, 0x88, 0xcc, 0x35, 0x33, 0x3e, 0x1a, 0x0d, 0x27, 0xee, 0xf4, - 0xe9, 0xed, 0x5d, 0x6f, 0xb0, 0x48, 0x9d, 0x86, 0xc8, 0x8c, 0x99, 0xf1, 0x07, 0x84, 0xef, 0xdf, - 0xc0, 0x7b, 0xc7, 0xf8, 0xb0, 0xf9, 0x05, 0xe7, 0x64, 0xb1, 0xa8, 0x98, 0xd6, 0x3e, 0x1a, 0xa1, - 0xc9, 0x7e, 0x7a, 0x6f, 0x47, 0x3c, 0xef, 0x70, 0x2f, 0xc0, 0xae, 0xad, 0xaf, 0x6a, 0x9a, 0xef, - 0x1e, 0x34, 0xdd, 0xa7, 0x52, 0x27, 0x35, 0xb5, 0x66, 0x8f, 0xf1, 0x41, 0x03, 0x36, 0x4d, 0xae, - 0xe0, 0x1d, 0xab, 0xb6, 0x1b, 0xdd, 0x4d, 0xdd, 0x0e, 0x4b, 0x2c, 0x34, 0xbf, 0xf8, 0xb2, 0x0e, - 0xd0, 0xf5, 0x3a, 0x40, 0x3f, 0xd6, 0x01, 0xfa, 0xb4, 0x09, 0x06, 0xd7, 0x9b, 0x60, 0xf0, 0x75, - 0x13, 0x0c, 0x5e, 0xcf, 0xfe, 0x35, 0xe3, 0xfb, 0x3f, 0xae, 0xd4, 0xb4, 0x8a, 0x69, 0xea, 0x6c, - 0x2f, 0xee, 0xe4, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x90, 0xb1, 0x5b, 0xcb, 0x02, 0x00, - 0x00, + // 524 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x4f, 0x8b, 0xd3, 0x40, + 0x18, 0xc6, 0x77, 0xda, 0x35, 0x6b, 0xa7, 0x2b, 0xb8, 0x51, 0x96, 0xa0, 0x98, 0xad, 0x3d, 0x48, + 0x61, 0x31, 0xa1, 0x5d, 0x0a, 0xee, 0x61, 0x0f, 0x16, 0x14, 0x61, 0xc1, 0x2d, 0x29, 0x56, 0xf0, + 0x12, 0x33, 0xe9, 0x6c, 0x32, 0x74, 0x9a, 0x77, 0xc8, 0x4c, 0xe2, 0xe6, 0xe6, 0xc5, 0x9b, 0x07, + 0x3f, 0x81, 0x9f, 0xc7, 0xe3, 0x1e, 0xc5, 0x83, 0x48, 0xfb, 0x45, 0x64, 0x92, 0xb8, 0xf8, 0xa7, + 0x45, 0x10, 0x6f, 0x99, 0xe7, 0x79, 0xf3, 0xcc, 0xef, 0x7d, 0x67, 0x06, 0x3f, 0x20, 0x01, 0x29, + 0x38, 0x24, 0x6e, 0x18, 0xd3, 0x70, 0x2e, 0x80, 0x25, 0x8a, 0x25, 0x91, 0x9b, 0xf7, 0x5d, 0xc2, + 0xa5, 0x3f, 0xa7, 0x85, 0x23, 0x52, 0x50, 0x60, 0x5a, 0x75, 0x9d, 0xf3, 0x4b, 0x9d, 0x93, 0xf7, + 0xef, 0xdc, 0x8e, 0x20, 0x82, 0xb2, 0xc8, 0xd5, 0x5f, 0x55, 0x7d, 0xf7, 0x23, 0xc2, 0xc6, 0x88, + 0xcb, 0x53, 0x5a, 0x98, 0x2f, 0xb0, 0x21, 0x32, 0x32, 0xa7, 0x85, 0x85, 0x3a, 0xa8, 0xb7, 0x3b, + 0x3a, 0xf9, 0xf2, 0xf5, 0xe0, 0x38, 0x62, 0x2a, 0xce, 0x88, 0x13, 0xc2, 0xc2, 0xad, 0x93, 0xc3, + 0x38, 0x60, 0x89, 0x7b, 0x85, 0x93, 0x16, 0x42, 0x81, 0x86, 0xe8, 0x0f, 0x8e, 0x1e, 0xf5, 0x9d, + 0x71, 0x46, 0x38, 0x0b, 0x4f, 0x69, 0xe1, 0xd5, 0x61, 0xe6, 0x09, 0x6e, 0x0a, 0x10, 0x56, 0xa3, + 0x83, 0x7a, 0xed, 0xc1, 0xa1, 0xb3, 0x89, 0xcf, 0x19, 0xa7, 0x00, 0xe7, 0x67, 0xe7, 0x63, 0x90, + 0x92, 0x4a, 0xc9, 0x20, 0xf1, 0xf4, 0x7f, 0xdd, 0xf7, 0x08, 0xef, 0xfd, 0x61, 0x99, 0x07, 0xb8, + 0x4d, 0x67, 0x83, 0xe1, 0xb0, 0x7f, 0xec, 0x4b, 0x16, 0x55, 0xc0, 0x1e, 0xae, 0xa5, 0x09, 0x8b, + 0xcc, 0x29, 0xde, 0xd1, 0x83, 0xd1, 0x66, 0xe3, 0xdf, 0xbb, 0x99, 0xb0, 0x28, 0x09, 0x54, 0x96, + 0x52, 0xcf, 0x20, 0x5c, 0x4e, 0x58, 0xd4, 0x7d, 0x8d, 0xf7, 0xa7, 0x01, 0x67, 0xb3, 0x40, 0x41, + 0xfa, 0x92, 0xa9, 0xb8, 0x9a, 0xdd, 0x84, 0x2a, 0xf3, 0x29, 0xde, 0xc9, 0x03, 0xee, 0x4b, 0xaa, + 0x2c, 0xd4, 0x69, 0xf6, 0xda, 0x83, 0x87, 0x9b, 0x7b, 0x5d, 0x13, 0xe1, 0x19, 0x79, 0xc0, 0x27, + 0x54, 0x75, 0xdf, 0x21, 0x7c, 0x6b, 0x8d, 0x6f, 0x1e, 0xe2, 0xbd, 0xfc, 0x87, 0xec, 0x07, 0xb3, + 0x59, 0x4a, 0xa5, 0x2c, 0x1b, 0x6f, 0x79, 0x37, 0xaf, 0x8c, 0xc7, 0x95, 0x6e, 0xda, 0xb8, 0xad, + 0xdb, 0x17, 0x19, 0xd1, 0x77, 0xa3, 0x1a, 0x81, 0xd7, 0x22, 0x5c, 0x8e, 0x33, 0xa2, 0xc3, 0xee, + 0xe3, 0xdd, 0x1c, 0x34, 0x8d, 0x2f, 0xe0, 0x0d, 0x4d, 0xad, 0x66, 0x07, 0xf5, 0xb6, 0xbd, 0x76, + 0xa5, 0x8d, 0xb5, 0xd4, 0x7d, 0xdb, 0xc0, 0x37, 0xa6, 0xa0, 0xe8, 0x93, 0x0b, 0x45, 0x93, 0x72, + 0xe8, 0xfb, 0xd8, 0x90, 0x2c, 0x4a, 0x68, 0x5a, 0x6f, 0x5b, 0xaf, 0xd6, 0x93, 0x35, 0x36, 0x90, + 0xdd, 0xc3, 0x98, 0x70, 0x08, 0xe7, 0x7e, 0x1c, 0xc8, 0xb8, 0xdc, 0xb7, 0x04, 0x83, 0x70, 0xfe, + 0x2c, 0x90, 0xb1, 0x79, 0x17, 0xb7, 0xa8, 0x80, 0x30, 0xf6, 0x93, 0x6c, 0x61, 0x6d, 0x97, 0x54, + 0xd7, 0x4b, 0xe1, 0x79, 0xb6, 0xd0, 0x00, 0x31, 0x65, 0x51, 0xac, 0xac, 0x6b, 0xa5, 0x53, 0xaf, + 0x7e, 0x3e, 0x6c, 0xe3, 0x3f, 0x1e, 0xf6, 0xe8, 0xec, 0xd3, 0xd2, 0x46, 0x97, 0x4b, 0x1b, 0x7d, + 0x5b, 0xda, 0xe8, 0xc3, 0xca, 0xde, 0xba, 0x5c, 0xd9, 0x5b, 0x9f, 0x57, 0xf6, 0xd6, 0xab, 0xe1, + 0xdf, 0xc2, 0x2f, 0x7e, 0x7b, 0xa8, 0xaa, 0x10, 0x54, 0x12, 0xa3, 0x7c, 0x74, 0x47, 0xdf, 0x03, + 0x00, 0x00, 0xff, 0xff, 0xd2, 0x99, 0x6b, 0x42, 0xce, 0x03, 0x00, 0x00, } func (m *BlsKey) Marshal() (dAtA []byte, err error) { @@ -303,7 +396,7 @@ func (m *BlsKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintBlsKey(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 } if m.Pubkey != nil { { @@ -315,7 +408,7 @@ func (m *BlsKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintBlsKey(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -350,14 +443,14 @@ func (m *ProofOfPossession) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintBlsKey(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 } if len(m.Ed25519Sig) > 0 { i -= len(m.Ed25519Sig) copy(dAtA[i:], m.Ed25519Sig) i = encodeVarintBlsKey(dAtA, i, uint64(len(m.Ed25519Sig))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -441,6 +534,72 @@ func (m *ValidatorWithBlsKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *VoteExtension) 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 *VoteExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlsSig != nil { + { + size := m.BlsSig.Size() + i -= size + if _, err := m.BlsSig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintBlsKey(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Height != 0 { + i = encodeVarintBlsKey(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x28 + } + if m.EpochNum != 0 { + i = encodeVarintBlsKey(dAtA, i, uint64(m.EpochNum)) + i-- + dAtA[i] = 0x20 + } + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintBlsKey(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintBlsKey(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintBlsKey(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintBlsKey(dAtA []byte, offset int, v uint64) int { offset -= sovBlsKey(v) base := offset @@ -521,6 +680,37 @@ func (m *ValidatorWithBlsKey) Size() (n int) { return n } +func (m *VoteExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovBlsKey(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovBlsKey(uint64(l)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovBlsKey(uint64(l)) + } + if m.EpochNum != 0 { + n += 1 + sovBlsKey(uint64(m.EpochNum)) + } + if m.Height != 0 { + n += 1 + sovBlsKey(uint64(m.Height)) + } + if m.BlsSig != nil { + l = m.BlsSig.Size() + n += 1 + l + sovBlsKey(uint64(l)) + } + return n +} + func sovBlsKey(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -556,7 +746,7 @@ func (m *BlsKey) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: BlsKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 2: + case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pubkey", wireType) } @@ -591,7 +781,7 @@ func (m *BlsKey) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pop", wireType) } @@ -677,7 +867,7 @@ func (m *ProofOfPossession) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: ProofOfPossession: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 2: + case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ed25519Sig", wireType) } @@ -711,7 +901,7 @@ func (m *ProofOfPossession) Unmarshal(dAtA []byte) error { m.Ed25519Sig = []byte{} } iNdEx = postIndex - case 3: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BlsSig", wireType) } @@ -986,6 +1176,227 @@ func (m *ValidatorWithBlsKey) Unmarshal(dAtA []byte) error { } return nil } +func (m *VoteExtension) 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 ErrIntOverflowBlsKey + } + 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: VoteExtension: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VoteExtension: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + 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 ErrInvalidLengthBlsKey + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBlsKey + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + 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 ErrInvalidLengthBlsKey + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBlsKey + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBlsKey + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBlsKey + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = append(m.BlockHash[:0], dAtA[iNdEx:postIndex]...) + if m.BlockHash == nil { + m.BlockHash = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochNum", wireType) + } + m.EpochNum = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EpochNum |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlsSig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlsKey + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBlsKey + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBlsKey + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_crypto_bls12381.Signature + m.BlsSig = &v + if err := m.BlsSig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBlsKey(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBlsKey + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipBlsKey(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/checkpointing/types/checkpoint.pb.go b/x/checkpointing/types/checkpoint.pb.go index 587a53aaa..4d3fdb84c 100644 --- a/x/checkpointing/types/checkpoint.pb.go +++ b/x/checkpointing/types/checkpoint.pb.go @@ -7,6 +7,7 @@ import ( bytes "bytes" fmt "fmt" github_com_babylonchain_babylon_crypto_bls12381 "github.com/babylonchain/babylon/crypto/bls12381" + types "github.com/cometbft/cometbft/abci/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" @@ -69,13 +70,13 @@ func (CheckpointStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_73996df9c6aabde4, []int{0} } -// RawCheckpoint wraps the BLS multi sig with meta data +// RawCheckpoint wraps the BLS multi sig with metadata type RawCheckpoint struct { // epoch_num defines the epoch number the raw checkpoint is for EpochNum uint64 `protobuf:"varint,1,opt,name=epoch_num,json=epochNum,proto3" json:"epoch_num,omitempty"` - // last_commit_hash defines the 'LastCommitHash' that individual BLS sigs are - // signed on - LastCommitHash *LastCommitHash `protobuf:"bytes,2,opt,name=last_commit_hash,json=lastCommitHash,proto3,customtype=LastCommitHash" json:"last_commit_hash,omitempty"` + // block_hash defines the 'BlockID.Hash', which is the hash of + // the block that individual BLS sigs are signed on + BlockHash *BlockHash `protobuf:"bytes,2,opt,name=block_hash,json=blockHash,proto3,customtype=BlockHash" json:"block_hash,omitempty"` // bitmap defines the bitmap that indicates the signers of the BLS multi sig Bitmap []byte `protobuf:"bytes,3,opt,name=bitmap,proto3" json:"bitmap,omitempty"` // bls_multi_sig defines the multi sig that is aggregated from individual BLS @@ -130,7 +131,7 @@ func (m *RawCheckpoint) GetBitmap() []byte { return nil } -// RawCheckpointWithMeta wraps the raw checkpoint with meta data. +// RawCheckpointWithMeta wraps the raw checkpoint with metadata. type RawCheckpointWithMeta struct { Ckpt *RawCheckpoint `protobuf:"bytes,1,opt,name=ckpt,proto3" json:"ckpt,omitempty"` // status defines the status of the checkpoint @@ -206,6 +207,61 @@ func (m *RawCheckpointWithMeta) GetLifecycle() []*CheckpointStateUpdate { return nil } +// InjectedCheckpoint wraps the checkpoint and the extended votes +type InjectedCheckpoint struct { + Ckpt *RawCheckpointWithMeta `protobuf:"bytes,1,opt,name=ckpt,proto3" json:"ckpt,omitempty"` + // extended_commit_info is the commit info including the vote extensions + // from the previous proposal + ExtendedCommitInfo *types.ExtendedCommitInfo `protobuf:"bytes,2,opt,name=extended_commit_info,json=extendedCommitInfo,proto3" json:"extended_commit_info,omitempty"` +} + +func (m *InjectedCheckpoint) Reset() { *m = InjectedCheckpoint{} } +func (m *InjectedCheckpoint) String() string { return proto.CompactTextString(m) } +func (*InjectedCheckpoint) ProtoMessage() {} +func (*InjectedCheckpoint) Descriptor() ([]byte, []int) { + return fileDescriptor_73996df9c6aabde4, []int{2} +} +func (m *InjectedCheckpoint) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InjectedCheckpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InjectedCheckpoint.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 *InjectedCheckpoint) XXX_Merge(src proto.Message) { + xxx_messageInfo_InjectedCheckpoint.Merge(m, src) +} +func (m *InjectedCheckpoint) XXX_Size() int { + return m.Size() +} +func (m *InjectedCheckpoint) XXX_DiscardUnknown() { + xxx_messageInfo_InjectedCheckpoint.DiscardUnknown(m) +} + +var xxx_messageInfo_InjectedCheckpoint proto.InternalMessageInfo + +func (m *InjectedCheckpoint) GetCkpt() *RawCheckpointWithMeta { + if m != nil { + return m.Ckpt + } + return nil +} + +func (m *InjectedCheckpoint) GetExtendedCommitInfo() *types.ExtendedCommitInfo { + if m != nil { + return m.ExtendedCommitInfo + } + return nil +} + // CheckpointStateUpdate defines a state transition on the checkpoint. type CheckpointStateUpdate struct { // state defines the event of a state transition towards this state @@ -222,7 +278,7 @@ func (m *CheckpointStateUpdate) Reset() { *m = CheckpointStateUpdate{} } func (m *CheckpointStateUpdate) String() string { return proto.CompactTextString(m) } func (*CheckpointStateUpdate) ProtoMessage() {} func (*CheckpointStateUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_73996df9c6aabde4, []int{2} + return fileDescriptor_73996df9c6aabde4, []int{3} } func (m *CheckpointStateUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -272,25 +328,29 @@ func (m *CheckpointStateUpdate) GetBlockTime() *time.Time { return nil } -// BlsSig wraps the BLS sig with meta data. +// BlsSig wraps the BLS sig with metadata. type BlsSig struct { // epoch_num defines the epoch number that the BLS sig is signed on EpochNum uint64 `protobuf:"varint,1,opt,name=epoch_num,json=epochNum,proto3" json:"epoch_num,omitempty"` - // last_commit_hash defines the 'LastCommitHash' that the BLS sig is signed on - LastCommitHash *LastCommitHash `protobuf:"bytes,2,opt,name=last_commit_hash,json=lastCommitHash,proto3,customtype=LastCommitHash" json:"last_commit_hash,omitempty"` - BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,3,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` + // block_hash defines the 'BlockID.Hash', which is the hash of + // the block that individual BLS sigs are signed on + BlockHash *BlockHash `protobuf:"bytes,2,opt,name=block_hash,json=blockHash,proto3,customtype=BlockHash" json:"block_hash,omitempty"` + BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,3,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` // can't find cosmos_proto.scalar when compiling due to cosmos v0.45.4 does // not support scalar string signer_address = 4 [(cosmos_proto.scalar) = - // "cosmos.AddressString"]; the signer_address defines the address of the + // "cosmos.AddressString"] + // the signer_address defines the address of the // signer SignerAddress string `protobuf:"bytes,4,opt,name=signer_address,json=signerAddress,proto3" json:"signer_address,omitempty"` + // validator_address defines the validator's consensus address + ValidatorAddress string `protobuf:"bytes,5,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` } func (m *BlsSig) Reset() { *m = BlsSig{} } func (m *BlsSig) String() string { return proto.CompactTextString(m) } func (*BlsSig) ProtoMessage() {} func (*BlsSig) Descriptor() ([]byte, []int) { - return fileDescriptor_73996df9c6aabde4, []int{3} + return fileDescriptor_73996df9c6aabde4, []int{4} } func (m *BlsSig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -333,10 +393,18 @@ func (m *BlsSig) GetSignerAddress() string { return "" } +func (m *BlsSig) GetValidatorAddress() string { + if m != nil { + return m.ValidatorAddress + } + return "" +} + func init() { proto.RegisterEnum("babylon.checkpointing.v1.CheckpointStatus", CheckpointStatus_name, CheckpointStatus_value) proto.RegisterType((*RawCheckpoint)(nil), "babylon.checkpointing.v1.RawCheckpoint") proto.RegisterType((*RawCheckpointWithMeta)(nil), "babylon.checkpointing.v1.RawCheckpointWithMeta") + proto.RegisterType((*InjectedCheckpoint)(nil), "babylon.checkpointing.v1.InjectedCheckpoint") proto.RegisterType((*CheckpointStateUpdate)(nil), "babylon.checkpointing.v1.CheckpointStateUpdate") proto.RegisterType((*BlsSig)(nil), "babylon.checkpointing.v1.BlsSig") } @@ -346,55 +414,61 @@ func init() { } var fileDescriptor_73996df9c6aabde4 = []byte{ - // 764 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x94, 0x4d, 0x8b, 0xdb, 0x46, - 0x18, 0xc7, 0xad, 0x5d, 0xc5, 0x8d, 0xc7, 0x6b, 0x23, 0x86, 0x6e, 0x31, 0x2e, 0xd8, 0xee, 0x42, - 0xa9, 0x9b, 0x83, 0x84, 0x1d, 0x0a, 0x7d, 0xa5, 0x95, 0x5f, 0xb6, 0x31, 0xb1, 0x9d, 0x45, 0xb2, - 0x5b, 0x08, 0x14, 0x31, 0x92, 0x67, 0x47, 0x83, 0x47, 0x1a, 0xa1, 0x19, 0x25, 0x75, 0x3f, 0x41, - 0xd9, 0x53, 0xbe, 0xc0, 0x42, 0xa1, 0x1f, 0xa5, 0x97, 0x1e, 0x73, 0x2c, 0x39, 0xa4, 0x65, 0xf7, - 0x92, 0xb6, 0x5f, 0xa2, 0x68, 0xe4, 0xbc, 0x38, 0xe9, 0xd2, 0x17, 0xda, 0x9b, 0xe6, 0xef, 0xff, - 0xff, 0x61, 0x9e, 0xdf, 0x33, 0x8f, 0xc1, 0xbb, 0x3e, 0xf2, 0x37, 0x8c, 0xc7, 0x56, 0x10, 0xe2, - 0x60, 0x9d, 0x70, 0x1a, 0x4b, 0x1a, 0x13, 0xeb, 0x5e, 0xef, 0x05, 0xc1, 0x4c, 0x52, 0x2e, 0x39, - 0x6c, 0x6c, 0xad, 0xe6, 0x8e, 0xd5, 0xbc, 0xd7, 0x6b, 0xb6, 0x09, 0xe7, 0x84, 0x61, 0x4b, 0xf9, - 0xfc, 0xec, 0xd4, 0x92, 0x34, 0xc2, 0x42, 0xa2, 0x28, 0x29, 0xa2, 0xcd, 0xd7, 0x09, 0x27, 0x5c, - 0x7d, 0x5a, 0xf9, 0x57, 0xa1, 0x1e, 0xfd, 0xae, 0x81, 0x9a, 0x83, 0xee, 0x0f, 0x9f, 0x95, 0x83, - 0x6f, 0x82, 0x0a, 0x4e, 0x78, 0x10, 0x7a, 0x71, 0x16, 0x35, 0xb4, 0x8e, 0xd6, 0xd5, 0x9d, 0xeb, - 0x4a, 0x98, 0x67, 0x11, 0xfc, 0x18, 0x18, 0x0c, 0x09, 0xe9, 0x05, 0x3c, 0x8a, 0xa8, 0xf4, 0x42, - 0x24, 0xc2, 0xc6, 0x5e, 0x47, 0xeb, 0x1e, 0x0c, 0xe0, 0xa3, 0xc7, 0xed, 0xfa, 0x14, 0x09, 0x39, - 0x54, 0x3f, 0xdd, 0x42, 0x22, 0x74, 0xea, 0x6c, 0xe7, 0x0c, 0xdf, 0x00, 0x65, 0x9f, 0xca, 0x08, - 0x25, 0x8d, 0xfd, 0x3c, 0xe3, 0x6c, 0x4f, 0x10, 0x81, 0x9a, 0xcf, 0x84, 0x17, 0x65, 0x4c, 0x52, - 0x4f, 0x50, 0xd2, 0xd0, 0x55, 0xc9, 0x4f, 0x1e, 0x3d, 0x6e, 0x7f, 0x40, 0xa8, 0x0c, 0x33, 0xdf, - 0x0c, 0x78, 0x64, 0x6d, 0x7b, 0x0f, 0x42, 0x44, 0x63, 0xeb, 0x19, 0xb3, 0x74, 0x93, 0x48, 0x6e, - 0xf9, 0x4c, 0xf4, 0xfa, 0x37, 0xdf, 0xef, 0x99, 0x2e, 0x25, 0x31, 0x92, 0x59, 0x8a, 0x9d, 0xaa, - 0xcf, 0xc4, 0x2c, 0x2f, 0xe9, 0x52, 0xf2, 0xa1, 0xfe, 0xe4, 0xbb, 0xb6, 0x76, 0xf4, 0xeb, 0x1e, - 0x38, 0xdc, 0xe9, 0xf6, 0x4b, 0x2a, 0xc3, 0x19, 0x96, 0x08, 0x7e, 0x04, 0xf4, 0x60, 0x9d, 0x48, - 0xd5, 0x70, 0xb5, 0xff, 0x8e, 0x79, 0x15, 0x67, 0x73, 0x27, 0xee, 0xa8, 0x10, 0x1c, 0x80, 0xb2, - 0x90, 0x48, 0x66, 0x42, 0xb1, 0xa8, 0xf7, 0x6f, 0x5c, 0x1d, 0x7f, 0x9e, 0x75, 0x55, 0xc2, 0xd9, - 0x26, 0xe1, 0x57, 0x20, 0xbf, 0xaf, 0x87, 0x08, 0x49, 0xbd, 0x64, 0x5d, 0x00, 0xfa, 0x77, 0x04, - 0x4e, 0x32, 0x9f, 0xd1, 0xe0, 0x36, 0xde, 0x38, 0x15, 0x9f, 0x09, 0x9b, 0x90, 0xf4, 0x64, 0x9d, - 0x4f, 0x35, 0xe1, 0xf7, 0x71, 0xea, 0x89, 0x2c, 0x52, 0x78, 0x75, 0xe7, 0xba, 0x12, 0xdc, 0x2c, - 0x82, 0x33, 0x50, 0x61, 0xf4, 0x14, 0x07, 0x9b, 0x80, 0xe1, 0xc6, 0xb5, 0xce, 0x7e, 0xb7, 0xda, - 0xb7, 0xfe, 0x6e, 0x0b, 0x78, 0x99, 0xac, 0x90, 0xc4, 0xce, 0xf3, 0x0a, 0x5b, 0xd6, 0x3f, 0x68, - 0xe0, 0xf0, 0x4f, 0xad, 0xf0, 0x33, 0x70, 0x2d, 0x6f, 0x1a, 0x2b, 0xd8, 0xff, 0x8c, 0x56, 0x11, - 0x84, 0x6f, 0x81, 0x03, 0x9f, 0xf1, 0x60, 0xed, 0x85, 0x98, 0x92, 0x50, 0x2a, 0xec, 0x7a, 0x3e, - 0x70, 0x1e, 0xac, 0x6f, 0x29, 0x09, 0x7e, 0x0a, 0x40, 0x61, 0xc9, 0xf7, 0x40, 0xe1, 0xac, 0xf6, - 0x9b, 0x66, 0xb1, 0x24, 0xe6, 0xd3, 0x25, 0x31, 0x17, 0x4f, 0x97, 0x64, 0xa0, 0x3f, 0xf8, 0xb9, - 0xad, 0xe5, 0xc4, 0x78, 0xb0, 0xce, 0xd5, 0x6d, 0x17, 0x4f, 0x34, 0x50, 0x1e, 0x30, 0xe1, 0x52, - 0xf2, 0x7f, 0x2e, 0xc6, 0x17, 0xe0, 0xb5, 0x7c, 0xf8, 0xf9, 0xd3, 0xdf, 0xff, 0x2f, 0x9e, 0x7e, - 0xd9, 0x2f, 0xae, 0xfc, 0x36, 0xa8, 0x0b, 0x4a, 0x62, 0x9c, 0x7a, 0x68, 0xb5, 0x4a, 0xb1, 0x10, - 0x6a, 0xf4, 0x15, 0xa7, 0x56, 0xa8, 0x76, 0x21, 0xaa, 0x56, 0x4b, 0x37, 0x7e, 0xd3, 0x80, 0xf1, - 0x32, 0x70, 0x68, 0x82, 0xc6, 0xf0, 0xf6, 0xc9, 0xc2, 0x73, 0x17, 0xf6, 0x62, 0xe9, 0x7a, 0xf6, - 0x70, 0xb8, 0x9c, 0x2d, 0xa7, 0xf6, 0x62, 0x32, 0xff, 0xdc, 0x28, 0x35, 0x8d, 0xb3, 0xf3, 0xce, - 0x81, 0x1d, 0x04, 0x59, 0x94, 0x31, 0x94, 0x0f, 0x0d, 0x1e, 0x01, 0xf8, 0xa2, 0xdf, 0x1d, 0xdb, - 0xd3, 0xf1, 0xc8, 0xd0, 0x9a, 0xe0, 0xec, 0xbc, 0x53, 0x76, 0x31, 0x62, 0x78, 0x05, 0xbb, 0xe0, - 0x70, 0xc7, 0xb3, 0x1c, 0xcc, 0x26, 0x8b, 0xc5, 0x78, 0x64, 0xec, 0x35, 0x6b, 0x67, 0xe7, 0x9d, - 0x8a, 0x9b, 0xf9, 0x11, 0x95, 0xf2, 0x55, 0xe7, 0xf0, 0xce, 0xfc, 0x78, 0xe2, 0xcc, 0xc6, 0x23, - 0x63, 0xbf, 0x70, 0x0e, 0x79, 0x7c, 0x4a, 0xd3, 0xe8, 0x55, 0xe7, 0xf1, 0x64, 0x6e, 0x4f, 0x27, - 0x77, 0xc7, 0x23, 0x43, 0x2f, 0x9c, 0xc7, 0x34, 0x46, 0x8c, 0x7e, 0x83, 0x57, 0x4d, 0xfd, 0xdb, - 0xef, 0x5b, 0xa5, 0xc1, 0x9d, 0x1f, 0x2f, 0x5a, 0xda, 0xc3, 0x8b, 0x96, 0xf6, 0xcb, 0x45, 0x4b, - 0x7b, 0x70, 0xd9, 0x2a, 0x3d, 0xbc, 0x6c, 0x95, 0x7e, 0xba, 0x6c, 0x95, 0xee, 0xbe, 0xf7, 0x57, - 0xd8, 0xbf, 0x7e, 0xe9, 0x7f, 0x5a, 0x6e, 0x12, 0x2c, 0xfc, 0xb2, 0x7a, 0x53, 0x37, 0xff, 0x08, - 0x00, 0x00, 0xff, 0xff, 0xad, 0xde, 0x97, 0x7b, 0xcd, 0x05, 0x00, 0x00, + // 853 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4f, 0x6f, 0xe3, 0x44, + 0x1c, 0x8d, 0x5b, 0x37, 0x6c, 0x26, 0xcd, 0x2a, 0x8c, 0xb6, 0x28, 0xca, 0x4a, 0x49, 0x28, 0x42, + 0x94, 0x05, 0xd9, 0x6a, 0x56, 0x48, 0xfc, 0x11, 0x82, 0xc4, 0x4d, 0x21, 0xda, 0xa6, 0x5b, 0xd9, + 0x09, 0x48, 0x2b, 0x21, 0x6b, 0x6c, 0x4f, 0xec, 0x21, 0xb6, 0xc7, 0xf2, 0x8c, 0xbb, 0x1b, 0xee, + 0x48, 0xa8, 0xa7, 0xfd, 0x02, 0x95, 0x90, 0xf8, 0x02, 0x7c, 0x07, 0x2e, 0x1c, 0xf7, 0x88, 0x16, + 0x69, 0x41, 0xed, 0x05, 0xf8, 0x14, 0x68, 0xc6, 0x4e, 0xd3, 0x6c, 0x59, 0xb1, 0xa0, 0xde, 0x26, + 0xcf, 0xef, 0x4d, 0x66, 0xde, 0xef, 0x3d, 0x0d, 0x78, 0xdb, 0x41, 0xce, 0x3c, 0xa4, 0xb1, 0xee, + 0x06, 0xd8, 0x9d, 0x25, 0x94, 0xc4, 0x9c, 0xc4, 0xbe, 0x7e, 0xbc, 0x7b, 0x09, 0xd0, 0x92, 0x94, + 0x72, 0x0a, 0x1b, 0x05, 0x55, 0x5b, 0xa1, 0x6a, 0xc7, 0xbb, 0xcd, 0xb6, 0x4f, 0xa9, 0x1f, 0x62, + 0x5d, 0xf2, 0x9c, 0x6c, 0xaa, 0x73, 0x12, 0x61, 0xc6, 0x51, 0x94, 0xe4, 0xd2, 0xe6, 0x2d, 0x9f, + 0xfa, 0x54, 0x2e, 0x75, 0xb1, 0x2a, 0xd0, 0xdb, 0x1c, 0xc7, 0x1e, 0x4e, 0x23, 0x12, 0x73, 0x1d, + 0x39, 0x2e, 0xd1, 0xf9, 0x3c, 0xc1, 0x2c, 0xff, 0xb8, 0xfd, 0xab, 0x02, 0x6a, 0x26, 0x7a, 0x68, + 0x5c, 0xfc, 0x17, 0xbc, 0x0d, 0x2a, 0x38, 0xa1, 0x6e, 0x60, 0xc7, 0x59, 0xd4, 0x50, 0x3a, 0xca, + 0x8e, 0x6a, 0xde, 0x90, 0xc0, 0x61, 0x16, 0xc1, 0x77, 0x01, 0x70, 0x42, 0xea, 0xce, 0xec, 0x00, + 0xb1, 0xa0, 0xb1, 0xd6, 0x51, 0x76, 0x36, 0xfb, 0xb5, 0xa7, 0xcf, 0xda, 0x95, 0xbe, 0x40, 0x3f, + 0x47, 0x2c, 0x30, 0x2b, 0xce, 0x62, 0x09, 0x5f, 0x03, 0x65, 0x87, 0xf0, 0x08, 0x25, 0x8d, 0x75, + 0xc1, 0x34, 0x8b, 0x5f, 0x10, 0x81, 0x9a, 0x13, 0x32, 0x3b, 0xca, 0x42, 0x4e, 0x6c, 0x46, 0xfc, + 0x86, 0x2a, 0x37, 0xfa, 0xf8, 0xe9, 0xb3, 0xf6, 0x07, 0x3e, 0xe1, 0x41, 0xe6, 0x68, 0x2e, 0x8d, + 0xf4, 0xc2, 0x08, 0x37, 0x40, 0x24, 0xd6, 0x2f, 0x0c, 0x4c, 0xe7, 0x09, 0xa7, 0xba, 0x13, 0xb2, + 0xdd, 0xee, 0xdd, 0xf7, 0x77, 0x35, 0x8b, 0xf8, 0x31, 0xe2, 0x59, 0x8a, 0xcd, 0xaa, 0x13, 0xb2, + 0x91, 0xd8, 0xd2, 0x22, 0xfe, 0x87, 0xea, 0x1f, 0xdf, 0xb7, 0x95, 0xed, 0x3f, 0xd7, 0xc0, 0xd6, + 0xca, 0xed, 0xbe, 0x24, 0x3c, 0x18, 0x61, 0x8e, 0xe0, 0x47, 0x40, 0x75, 0x67, 0x09, 0x97, 0x17, + 0xac, 0x76, 0xdf, 0xd2, 0x5e, 0x64, 0xba, 0xb6, 0x22, 0x37, 0xa5, 0x08, 0xf6, 0x41, 0x99, 0x71, + 0xc4, 0x33, 0x26, 0x1d, 0xb8, 0xd9, 0xbd, 0xf3, 0x62, 0xf9, 0x52, 0x6b, 0x49, 0x85, 0x59, 0x28, + 0xe1, 0x57, 0x40, 0x9c, 0xd7, 0x46, 0xbe, 0x9f, 0xda, 0xc9, 0x2c, 0x37, 0xe8, 0xff, 0x39, 0x70, + 0x94, 0x39, 0x21, 0x71, 0xef, 0xe1, 0xb9, 0xb0, 0x9e, 0xf5, 0x7c, 0x3f, 0x3d, 0x9a, 0x89, 0x29, + 0x26, 0xf4, 0x21, 0x4e, 0x6d, 0x96, 0x45, 0xd2, 0x5e, 0xd5, 0xbc, 0x21, 0x01, 0x2b, 0x8b, 0xe0, + 0x08, 0x54, 0x42, 0x32, 0xc5, 0xee, 0xdc, 0x0d, 0x71, 0x63, 0xa3, 0xb3, 0xbe, 0x53, 0xed, 0xea, + 0x2f, 0x7b, 0x05, 0x3c, 0x49, 0x3c, 0xc4, 0xb1, 0xb9, 0xdc, 0xa1, 0xf0, 0xfa, 0x47, 0x05, 0xc0, + 0x61, 0xfc, 0x35, 0x76, 0x39, 0xf6, 0x2e, 0xc5, 0xc9, 0x58, 0x31, 0x5a, 0x7f, 0x49, 0xa3, 0x17, + 0x73, 0x2a, 0x0c, 0x9f, 0x80, 0x5b, 0xf8, 0x91, 0x8c, 0xb1, 0x67, 0xbb, 0x34, 0x8a, 0x08, 0xb7, + 0x49, 0x3c, 0xa5, 0xd2, 0xfe, 0x6a, 0xf7, 0x0d, 0x6d, 0x99, 0x70, 0x4d, 0x24, 0x5c, 0x1b, 0x14, + 0x64, 0x43, 0x72, 0x87, 0xf1, 0x94, 0x9a, 0x10, 0x5f, 0xc1, 0xb6, 0x7f, 0x52, 0xc0, 0xd6, 0x3f, + 0xde, 0x0e, 0x7e, 0x0a, 0x36, 0xc4, 0x9c, 0xb0, 0x3c, 0xf6, 0x7f, 0x1b, 0x70, 0x2e, 0x84, 0xaf, + 0x83, 0xcd, 0xa2, 0x29, 0x98, 0xf8, 0x01, 0x97, 0x47, 0x55, 0x45, 0x46, 0x45, 0x39, 0x24, 0x04, + 0x3f, 0x59, 0x94, 0x49, 0xf4, 0x58, 0x26, 0xa0, 0xda, 0x6d, 0x6a, 0x79, 0xc9, 0xb5, 0x45, 0xc9, + 0xb5, 0xf1, 0xa2, 0xe4, 0x7d, 0xf5, 0xf1, 0x6f, 0x6d, 0xa5, 0xe8, 0x97, 0x40, 0x0b, 0xe3, 0xbf, + 0x5d, 0x03, 0xe5, 0x7e, 0xc8, 0x2c, 0xe2, 0x5f, 0x67, 0x77, 0xbf, 0x00, 0xaf, 0x88, 0x7c, 0x8a, + 0x76, 0xae, 0x5f, 0x47, 0x3b, 0xcb, 0x4e, 0x7e, 0xc4, 0x37, 0xc1, 0x4d, 0x46, 0xfc, 0x18, 0xa7, + 0x36, 0xf2, 0xbc, 0x14, 0x33, 0x26, 0xd3, 0x59, 0x31, 0x6b, 0x39, 0xda, 0xcb, 0x41, 0xf8, 0x0e, + 0x78, 0xf5, 0x18, 0x85, 0xc4, 0x43, 0x9c, 0x2e, 0x99, 0x1b, 0x92, 0x59, 0xbf, 0xf8, 0x50, 0x90, + 0xa5, 0x0f, 0xa5, 0x3b, 0x7f, 0x29, 0xa0, 0xfe, 0xfc, 0x34, 0xa0, 0x06, 0x1a, 0xc6, 0xbd, 0xa3, + 0xb1, 0x6d, 0x8d, 0x7b, 0xe3, 0x89, 0x65, 0xf7, 0x0c, 0x63, 0x32, 0x9a, 0x1c, 0xf4, 0xc6, 0xc3, + 0xc3, 0xcf, 0xea, 0xa5, 0x66, 0xfd, 0xe4, 0xb4, 0xb3, 0xd9, 0x73, 0xdd, 0x2c, 0xca, 0x42, 0x24, + 0x26, 0x0a, 0xb7, 0x01, 0xbc, 0xcc, 0xb7, 0x06, 0xbd, 0x83, 0xc1, 0x5e, 0x5d, 0x69, 0x82, 0x93, + 0xd3, 0x4e, 0xd9, 0xc2, 0x28, 0xc4, 0x1e, 0xdc, 0x01, 0x5b, 0x2b, 0x9c, 0x49, 0x7f, 0x34, 0x1c, + 0x8f, 0x07, 0x7b, 0xf5, 0xb5, 0x66, 0xed, 0xe4, 0xb4, 0x53, 0xb1, 0x32, 0x27, 0x22, 0x9c, 0x5f, + 0x65, 0x1a, 0xf7, 0x0f, 0xf7, 0x87, 0xe6, 0x68, 0xb0, 0x57, 0x5f, 0xcf, 0x99, 0x06, 0x8d, 0xa7, + 0x24, 0x8d, 0xae, 0x32, 0xf7, 0x87, 0x87, 0xbd, 0x83, 0xe1, 0x83, 0xc1, 0x5e, 0x5d, 0xcd, 0x99, + 0xfb, 0x24, 0x46, 0x21, 0xf9, 0x06, 0x7b, 0x4d, 0xf5, 0xbb, 0x1f, 0x5a, 0xa5, 0xfe, 0xfd, 0x9f, + 0xcf, 0x5a, 0xca, 0x93, 0xb3, 0x96, 0xf2, 0xfb, 0x59, 0x4b, 0x79, 0x7c, 0xde, 0x2a, 0x3d, 0x39, + 0x6f, 0x95, 0x7e, 0x39, 0x6f, 0x95, 0x1e, 0xbc, 0xf7, 0x6f, 0x33, 0x7a, 0xf4, 0xdc, 0x23, 0x24, + 0x9f, 0x03, 0xa7, 0x2c, 0x03, 0x77, 0xf7, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x97, 0x52, + 0x24, 0xaa, 0x06, 0x00, 0x00, } func (this *RawCheckpoint) Equal(that interface{}) bool { @@ -419,11 +493,11 @@ func (this *RawCheckpoint) Equal(that interface{}) bool { if this.EpochNum != that1.EpochNum { return false } - if that1.LastCommitHash == nil { - if this.LastCommitHash != nil { + if that1.BlockHash == nil { + if this.BlockHash != nil { return false } - } else if !this.LastCommitHash.Equal(*that1.LastCommitHash) { + } else if !this.BlockHash.Equal(*that1.BlockHash) { return false } if !bytes.Equal(this.Bitmap, that1.Bitmap) { @@ -556,11 +630,11 @@ func (m *RawCheckpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.LastCommitHash != nil { + if m.BlockHash != nil { { - size := m.LastCommitHash.Size() + size := m.BlockHash.Size() i -= size - if _, err := m.LastCommitHash.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.BlockHash.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintCheckpoint(dAtA, i, uint64(size)) @@ -647,6 +721,53 @@ func (m *RawCheckpointWithMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *InjectedCheckpoint) 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 *InjectedCheckpoint) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InjectedCheckpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ExtendedCommitInfo != nil { + { + size, err := m.ExtendedCommitInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCheckpoint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Ckpt != nil { + { + size, err := m.Ckpt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCheckpoint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *CheckpointStateUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -668,12 +789,12 @@ func (m *CheckpointStateUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { var l int _ = l if m.BlockTime != nil { - n2, err2 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.BlockTime):]) - if err2 != nil { - return 0, err2 + n4, err4 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.BlockTime):]) + if err4 != nil { + return 0, err4 } - i -= n2 - i = encodeVarintCheckpoint(dAtA, i, uint64(n2)) + i -= n4 + i = encodeVarintCheckpoint(dAtA, i, uint64(n4)) i-- dAtA[i] = 0x1a } @@ -710,6 +831,13 @@ func (m *BlsSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintCheckpoint(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x2a + } if len(m.SignerAddress) > 0 { i -= len(m.SignerAddress) copy(dAtA[i:], m.SignerAddress) @@ -729,11 +857,11 @@ func (m *BlsSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.LastCommitHash != nil { + if m.BlockHash != nil { { - size := m.LastCommitHash.Size() + size := m.BlockHash.Size() i -= size - if _, err := m.LastCommitHash.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.BlockHash.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintCheckpoint(dAtA, i, uint64(size)) @@ -769,8 +897,8 @@ func (m *RawCheckpoint) Size() (n int) { if m.EpochNum != 0 { n += 1 + sovCheckpoint(uint64(m.EpochNum)) } - if m.LastCommitHash != nil { - l = m.LastCommitHash.Size() + if m.BlockHash != nil { + l = m.BlockHash.Size() n += 1 + l + sovCheckpoint(uint64(l)) } l = len(m.Bitmap) @@ -813,6 +941,23 @@ func (m *RawCheckpointWithMeta) Size() (n int) { return n } +func (m *InjectedCheckpoint) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Ckpt != nil { + l = m.Ckpt.Size() + n += 1 + l + sovCheckpoint(uint64(l)) + } + if m.ExtendedCommitInfo != nil { + l = m.ExtendedCommitInfo.Size() + n += 1 + l + sovCheckpoint(uint64(l)) + } + return n +} + func (m *CheckpointStateUpdate) Size() (n int) { if m == nil { return 0 @@ -841,8 +986,8 @@ func (m *BlsSig) Size() (n int) { if m.EpochNum != 0 { n += 1 + sovCheckpoint(uint64(m.EpochNum)) } - if m.LastCommitHash != nil { - l = m.LastCommitHash.Size() + if m.BlockHash != nil { + l = m.BlockHash.Size() n += 1 + l + sovCheckpoint(uint64(l)) } if m.BlsSig != nil { @@ -853,6 +998,10 @@ func (m *BlsSig) Size() (n int) { if l > 0 { n += 1 + l + sovCheckpoint(uint64(l)) } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovCheckpoint(uint64(l)) + } return n } @@ -912,7 +1061,7 @@ func (m *RawCheckpoint) Unmarshal(dAtA []byte) error { } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastCommitHash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -939,9 +1088,9 @@ func (m *RawCheckpoint) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v LastCommitHash - m.LastCommitHash = &v - if err := m.LastCommitHash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + var v BlockHash + m.BlockHash = &v + if err := m.BlockHash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1228,6 +1377,128 @@ func (m *RawCheckpointWithMeta) Unmarshal(dAtA []byte) error { } return nil } +func (m *InjectedCheckpoint) 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 ErrIntOverflowCheckpoint + } + 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: InjectedCheckpoint: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InjectedCheckpoint: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ckpt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCheckpoint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCheckpoint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCheckpoint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Ckpt == nil { + m.Ckpt = &RawCheckpointWithMeta{} + } + if err := m.Ckpt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendedCommitInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCheckpoint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCheckpoint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCheckpoint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExtendedCommitInfo == nil { + m.ExtendedCommitInfo = &types.ExtendedCommitInfo{} + } + if err := m.ExtendedCommitInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCheckpoint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCheckpoint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *CheckpointStateUpdate) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -1402,7 +1673,7 @@ func (m *BlsSig) Unmarshal(dAtA []byte) error { } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastCommitHash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -1429,9 +1700,9 @@ func (m *BlsSig) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v LastCommitHash - m.LastCommitHash = &v - if err := m.LastCommitHash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + var v BlockHash + m.BlockHash = &v + if err := m.BlockHash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1502,6 +1773,38 @@ func (m *BlsSig) Unmarshal(dAtA []byte) error { } m.SignerAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCheckpoint + } + 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 ErrInvalidLengthCheckpoint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCheckpoint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCheckpoint(dAtA[iNdEx:]) diff --git a/x/checkpointing/types/codec.go b/x/checkpointing/types/codec.go index cd1a2a495..5faec792e 100644 --- a/x/checkpointing/types/codec.go +++ b/x/checkpointing/types/codec.go @@ -14,7 +14,6 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { // Register messages registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgAddBlsSig{}, &MsgWrappedCreateValidator{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/checkpointing/types/errors.go b/x/checkpointing/types/errors.go index 722b11db7..f212924c6 100644 --- a/x/checkpointing/types/errors.go +++ b/x/checkpointing/types/errors.go @@ -4,18 +4,19 @@ import errorsmod "cosmossdk.io/errors" // x/checkpointing module sentinel errors var ( - ErrCkptDoesNotExist = errorsmod.Register(ModuleName, 1201, "raw checkpoint does not exist") - ErrCkptAlreadyExist = errorsmod.Register(ModuleName, 1202, "raw checkpoint already exists") - ErrCkptHashNotEqual = errorsmod.Register(ModuleName, 1203, "hash does not equal to raw checkpoint") - ErrCkptNotAccumulating = errorsmod.Register(ModuleName, 1204, "raw checkpoint is no longer accumulating BLS sigs") - ErrCkptAlreadyVoted = errorsmod.Register(ModuleName, 1205, "raw checkpoint already accumulated the validator") - ErrInvalidRawCheckpoint = errorsmod.Register(ModuleName, 1206, "raw checkpoint is invalid") - ErrInvalidCkptStatus = errorsmod.Register(ModuleName, 1207, "raw checkpoint's status is invalid") - ErrInvalidPoP = errorsmod.Register(ModuleName, 1208, "proof-of-possession is invalid") - ErrBlsKeyDoesNotExist = errorsmod.Register(ModuleName, 1209, "BLS public key does not exist") - ErrBlsKeyAlreadyExist = errorsmod.Register(ModuleName, 1210, "BLS public key already exists") - ErrBlsPrivKeyDoesNotExist = errorsmod.Register(ModuleName, 1211, "BLS private key does not exist") - ErrInvalidBlsSignature = errorsmod.Register(ModuleName, 1212, "BLS signature is invalid") - ErrConflictingCheckpoint = errorsmod.Register(ModuleName, 1213, "Conflicting checkpoint is found") - ErrInvalidLastCommitHash = errorsmod.Register(ModuleName, 1214, "Provided last commit hash is Invalid") + ErrCkptDoesNotExist = errorsmod.Register(ModuleName, 1201, "raw checkpoint does not exist") + ErrCkptAlreadyExist = errorsmod.Register(ModuleName, 1202, "raw checkpoint already exists") + ErrCkptHashNotEqual = errorsmod.Register(ModuleName, 1203, "hash does not equal to raw checkpoint") + ErrCkptNotAccumulating = errorsmod.Register(ModuleName, 1204, "raw checkpoint is no longer accumulating BLS sigs") + ErrCkptAlreadyVoted = errorsmod.Register(ModuleName, 1205, "raw checkpoint already accumulated the validator") + ErrInvalidRawCheckpoint = errorsmod.Register(ModuleName, 1206, "raw checkpoint is invalid") + ErrInvalidCkptStatus = errorsmod.Register(ModuleName, 1207, "raw checkpoint's status is invalid") + ErrInvalidPoP = errorsmod.Register(ModuleName, 1208, "proof-of-possession is invalid") + ErrBlsKeyDoesNotExist = errorsmod.Register(ModuleName, 1209, "BLS public key does not exist") + ErrBlsKeyAlreadyExist = errorsmod.Register(ModuleName, 1210, "BLS public key already exists") + ErrBlsPrivKeyDoesNotExist = errorsmod.Register(ModuleName, 1211, "BLS private key does not exist") + ErrInvalidBlsSignature = errorsmod.Register(ModuleName, 1212, "BLS signature is invalid") + ErrConflictingCheckpoint = errorsmod.Register(ModuleName, 1213, "Conflicting checkpoint is found") + ErrInvalidAppHash = errorsmod.Register(ModuleName, 1214, "Provided app hash is Invalid") + ErrInsufficientVotingPower = errorsmod.Register(ModuleName, 1215, "Accumulated voting power is not greater than 2/3 of total power") ) diff --git a/x/checkpointing/types/expected_keepers.go b/x/checkpointing/types/expected_keepers.go index 36eda39a8..329cdb67f 100644 --- a/x/checkpointing/types/expected_keepers.go +++ b/x/checkpointing/types/expected_keepers.go @@ -1,31 +1,24 @@ package types import ( - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + "context" + + cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// AccountKeeper defines the expected account keeper used for simulations (noalias) -type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI - // Methods imported from account should be defined here -} -// BankKeeper defines the expected interface needed to retrieve account balances. -type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here -} + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" +) // EpochingKeeper defines the expected interface needed to retrieve epoch info type EpochingKeeper interface { - GetEpoch(ctx sdk.Context) *epochingtypes.Epoch - EnqueueMsg(ctx sdk.Context, msg epochingtypes.QueuedMessage) - GetValidatorSet(ctx sdk.Context, epochNumer uint64) epochingtypes.ValidatorSet - GetTotalVotingPower(ctx sdk.Context, epochNumber uint64) int64 - CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCreateValidator) error + GetEpoch(ctx context.Context) *epochingtypes.Epoch + EnqueueMsg(ctx context.Context, msg epochingtypes.QueuedMessage) + GetAppHash(ctx context.Context, height uint64) ([]byte, error) + GetValidatorSet(ctx context.Context, epochNumer uint64) epochingtypes.ValidatorSet + GetTotalVotingPower(ctx context.Context, epochNumber uint64) int64 + CheckMsgCreateValidator(ctx context.Context, msg *stakingtypes.MsgCreateValidator) error + GetPubKeyByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (cmtprotocrypto.PublicKey, error) } // Event Hooks @@ -36,9 +29,9 @@ type EpochingKeeper interface { // CheckpointingHooks event hooks for raw checkpoint object (noalias) type CheckpointingHooks interface { - AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error // Must be called when a BLS key is registered - AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error // Must be called when a raw checkpoint is CONFIRMED - AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *RawCheckpoint) error // Must be called when a raw checkpoint is FORGOTTEN - AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error // Must be called when a raw checkpoint is FINALIZED - AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *RawCheckpoint) error // Must be called when a raw checkpoint's multi-sig is verified + AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error // Must be called when a BLS key is registered + AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error // Must be called when a raw checkpoint is CONFIRMED + AfterRawCheckpointForgotten(ctx context.Context, ckpt *RawCheckpoint) error // Must be called when a raw checkpoint is FORGOTTEN + AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error // Must be called when a raw checkpoint is FINALIZED + AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *RawCheckpoint) error // Must be called when a raw checkpoint's multi-sig is verified } diff --git a/x/checkpointing/types/genesis.go b/x/checkpointing/types/genesis.go index 0f00a9af9..1ab34122e 100644 --- a/x/checkpointing/types/genesis.go +++ b/x/checkpointing/types/genesis.go @@ -14,9 +14,6 @@ import ( "github.com/babylonchain/babylon/crypto/bls12381" ) -// DefaultIndex is the default capability global index -const DefaultIndex uint64 = 1 - // DefaultGenesis returns the default Capability genesis state func DefaultGenesis() *GenesisState { return &GenesisState{} diff --git a/x/checkpointing/types/hooks.go b/x/checkpointing/types/hooks.go index 50a97c406..3d8008870 100644 --- a/x/checkpointing/types/hooks.go +++ b/x/checkpointing/types/hooks.go @@ -1,6 +1,7 @@ package types import ( + "context" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -13,7 +14,7 @@ func NewMultiCheckpointingHooks(hooks ...CheckpointingHooks) MultiCheckpointingH return hooks } -func (h MultiCheckpointingHooks) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error { +func (h MultiCheckpointingHooks) AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error { for i := range h { if err := h[i].AfterBlsKeyRegistered(ctx, valAddr); err != nil { return err @@ -22,7 +23,7 @@ func (h MultiCheckpointingHooks) AfterBlsKeyRegistered(ctx sdk.Context, valAddr return nil } -func (h MultiCheckpointingHooks) AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error { +func (h MultiCheckpointingHooks) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { for i := range h { if err := h[i].AfterRawCheckpointConfirmed(ctx, epoch); err != nil { return err @@ -31,14 +32,14 @@ func (h MultiCheckpointingHooks) AfterRawCheckpointConfirmed(ctx sdk.Context, ep return nil } -func (h MultiCheckpointingHooks) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *RawCheckpoint) error { +func (h MultiCheckpointingHooks) AfterRawCheckpointForgotten(ctx context.Context, ckpt *RawCheckpoint) error { for i := range h { return h[i].AfterRawCheckpointForgotten(ctx, ckpt) } return nil } -func (h MultiCheckpointingHooks) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { +func (h MultiCheckpointingHooks) AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error { for i := range h { if err := h[i].AfterRawCheckpointFinalized(ctx, epoch); err != nil { return err @@ -47,7 +48,7 @@ func (h MultiCheckpointingHooks) AfterRawCheckpointFinalized(ctx sdk.Context, ep return nil } -func (h MultiCheckpointingHooks) AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *RawCheckpoint) error { +func (h MultiCheckpointingHooks) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *RawCheckpoint) error { for i := range h { if err := h[i].AfterRawCheckpointBlsSigVerified(ctx, ckpt); err != nil { return err diff --git a/x/checkpointing/types/msgs.go b/x/checkpointing/types/msgs.go index 4e82fc200..4af049c62 100644 --- a/x/checkpointing/types/msgs.go +++ b/x/checkpointing/types/msgs.go @@ -3,31 +3,20 @@ package types import ( "errors" - "github.com/babylonchain/babylon/crypto/bls12381" codectypes "github.com/cosmos/cosmos-sdk/codec/types" ed255192 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/babylonchain/babylon/crypto/bls12381" ) var ( // Ensure that MsgInsertHeader implements all functions of the Msg interface - _ sdk.Msg = (*MsgAddBlsSig)(nil) + _ sdk.Msg = (*MsgWrappedCreateValidator)(nil) ) -func NewMsgAddBlsSig(signer sdk.AccAddress, epochNum uint64, lch LastCommitHash, sig bls12381.Signature, addr sdk.ValAddress) *MsgAddBlsSig { - return &MsgAddBlsSig{ - Signer: signer.String(), - BlsSig: &BlsSig{ - EpochNum: epochNum, - LastCommitHash: &lch, - BlsSig: &sig, - SignerAddress: addr.String(), - }, - } -} - func NewMsgWrappedCreateValidator(msgCreateVal *stakingtypes.MsgCreateValidator, blsPK *bls12381.PublicKey, pop *ProofOfPossession) (*MsgWrappedCreateValidator, error) { return &MsgWrappedCreateValidator{ Key: &BlsKey{ @@ -38,37 +27,6 @@ func NewMsgWrappedCreateValidator(msgCreateVal *stakingtypes.MsgCreateValidator, }, nil } -// ValidateBasic validates stateless message elements -func (m *MsgAddBlsSig) ValidateBasic() error { - // This function validates stateless message elements - _, err := sdk.ValAddressFromBech32(m.BlsSig.SignerAddress) - if err != nil { - return err - } - - err = m.BlsSig.BlsSig.ValidateBasic() - if err != nil { - return err - } - err = m.BlsSig.LastCommitHash.ValidateBasic() - if err != nil { - return err - } - - return nil -} - -func (m *MsgAddBlsSig) GetSigners() []sdk.AccAddress { - signer, err := sdk.AccAddressFromBech32(m.Signer) - if err != nil { - // Panic, since the GetSigners method is called after ValidateBasic - // which performs the same check. - panic(err) - } - - return []sdk.AccAddress{signer} -} - func (m *MsgWrappedCreateValidator) VerifyPoP(valPubkey cryptotypes.PubKey) bool { return m.Key.Pop.IsValid(*m.Key.Pubkey, valPubkey) } @@ -78,12 +36,8 @@ func (m *MsgWrappedCreateValidator) ValidateBasic() error { if m.MsgCreateValidator == nil { return errors.New("MsgCreateValidator is nil") } - err := m.MsgCreateValidator.ValidateBasic() - if err != nil { - return err - } var pubKey ed255192.PubKey - err = pubKey.Unmarshal(m.MsgCreateValidator.Pubkey.GetValue()) + err := pubKey.Unmarshal(m.MsgCreateValidator.Pubkey.GetValue()) if err != nil { return err } @@ -95,10 +49,6 @@ func (m *MsgWrappedCreateValidator) ValidateBasic() error { return nil } -func (m *MsgWrappedCreateValidator) GetSigners() []sdk.AccAddress { - return m.MsgCreateValidator.GetSigners() -} - // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces // Needed since msg.MsgCreateValidator.Pubkey is in type Any func (msg MsgWrappedCreateValidator) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { diff --git a/x/checkpointing/types/msgs_test.go b/x/checkpointing/types/msgs_test.go index d92a753e1..f8191cce7 100644 --- a/x/checkpointing/types/msgs_test.go +++ b/x/checkpointing/types/msgs_test.go @@ -3,7 +3,7 @@ package types_test import ( "testing" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" appparams "github.com/babylonchain/babylon/app/params" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/privval" @@ -53,19 +53,19 @@ func TestMsgDecode(t *testing.T) { require.NotNil(t, msgWithType.MsgCreateValidator.Pubkey.GetCachedValue()) } -func buildMsgWrappedCreateValidatorWithAmount(addr sdk.AccAddress, bondTokens math.Int) (*types.MsgWrappedCreateValidator, error) { +func buildMsgWrappedCreateValidatorWithAmount(addr sdk.AccAddress, bondTokens sdkmath.Int) (*types.MsgWrappedCreateValidator, error) { tmValPrivkey := ed25519.GenPrivKey() bondCoin := sdk.NewCoin(appparams.DefaultBondDenom, bondTokens) description := stakingtypes.NewDescription("foo_moniker", "", "", "", "") - commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + commission := stakingtypes.NewCommissionRates(sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec()) - pk, err := cryptocodec.FromTmPubKeyInterface(tmValPrivkey.PubKey()) + pk, err := cryptocodec.FromCmtPubKeyInterface(tmValPrivkey.PubKey()) if err != nil { return nil, err } createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), pk, bondCoin, description, commission, sdk.OneInt(), + sdk.ValAddress(addr).String(), pk, bondCoin, description, commission, sdkmath.OneInt(), ) if err != nil { return nil, err diff --git a/x/checkpointing/types/pop_test.go b/x/checkpointing/types/pop_test.go index 3beeca1aa..3bd2aec76 100644 --- a/x/checkpointing/types/pop_test.go +++ b/x/checkpointing/types/pop_test.go @@ -14,7 +14,7 @@ func TestProofOfPossession_IsValid(t *testing.T) { blsPrivKey := bls12381.GenPrivKey() pop, err := privval.BuildPoP(valPrivKey, blsPrivKey) require.NoError(t, err) - valpk, err := codec.FromTmPubKeyInterface(valPrivKey.PubKey()) + valpk, err := codec.FromCmtPubKeyInterface(valPrivKey.PubKey()) require.NoError(t, err) require.True(t, pop.IsValid(blsPrivKey.PubKey(), valpk)) } diff --git a/x/checkpointing/types/tx.pb.go b/x/checkpointing/types/tx.pb.go index 83b25f08a..9a309d96a 100644 --- a/x/checkpointing/types/tx.pb.go +++ b/x/checkpointing/types/tx.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" types "github.com/cosmos/cosmos-sdk/x/staking/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" @@ -30,86 +30,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// MsgAddBlsSig defines a message to add a bls signature from a -// validator -type MsgAddBlsSig struct { - // signer corresponds to the submitter of the transaction - // This might be a different entity compared to the one that created the BLS signature - // (i.e. the validator owner of the BLS key which is specified by the `bls_sig.signer_address`) - Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` - BlsSig *BlsSig `protobuf:"bytes,2,opt,name=bls_sig,json=blsSig,proto3" json:"bls_sig,omitempty"` -} - -func (m *MsgAddBlsSig) Reset() { *m = MsgAddBlsSig{} } -func (m *MsgAddBlsSig) String() string { return proto.CompactTextString(m) } -func (*MsgAddBlsSig) ProtoMessage() {} -func (*MsgAddBlsSig) Descriptor() ([]byte, []int) { - return fileDescriptor_6b16c54750152c21, []int{0} -} -func (m *MsgAddBlsSig) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgAddBlsSig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgAddBlsSig.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 *MsgAddBlsSig) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgAddBlsSig.Merge(m, src) -} -func (m *MsgAddBlsSig) XXX_Size() int { - return m.Size() -} -func (m *MsgAddBlsSig) XXX_DiscardUnknown() { - xxx_messageInfo_MsgAddBlsSig.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgAddBlsSig proto.InternalMessageInfo - -// MsgAddBlsSigResponse defines the MsgAddBlsSig response type. -type MsgAddBlsSigResponse struct { -} - -func (m *MsgAddBlsSigResponse) Reset() { *m = MsgAddBlsSigResponse{} } -func (m *MsgAddBlsSigResponse) String() string { return proto.CompactTextString(m) } -func (*MsgAddBlsSigResponse) ProtoMessage() {} -func (*MsgAddBlsSigResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_6b16c54750152c21, []int{1} -} -func (m *MsgAddBlsSigResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgAddBlsSigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgAddBlsSigResponse.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 *MsgAddBlsSigResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgAddBlsSigResponse.Merge(m, src) -} -func (m *MsgAddBlsSigResponse) XXX_Size() int { - return m.Size() -} -func (m *MsgAddBlsSigResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgAddBlsSigResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgAddBlsSigResponse proto.InternalMessageInfo - // MsgWrappedCreateValidator defines a wrapped message to create a validator type MsgWrappedCreateValidator struct { Key *BlsKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` @@ -120,7 +40,7 @@ func (m *MsgWrappedCreateValidator) Reset() { *m = MsgWrappedCreateValid func (m *MsgWrappedCreateValidator) String() string { return proto.CompactTextString(m) } func (*MsgWrappedCreateValidator) ProtoMessage() {} func (*MsgWrappedCreateValidator) Descriptor() ([]byte, []int) { - return fileDescriptor_6b16c54750152c21, []int{2} + return fileDescriptor_6b16c54750152c21, []int{0} } func (m *MsgWrappedCreateValidator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -158,7 +78,7 @@ func (m *MsgWrappedCreateValidatorResponse) Reset() { *m = MsgWrappedCre func (m *MsgWrappedCreateValidatorResponse) String() string { return proto.CompactTextString(m) } func (*MsgWrappedCreateValidatorResponse) ProtoMessage() {} func (*MsgWrappedCreateValidatorResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_6b16c54750152c21, []int{3} + return fileDescriptor_6b16c54750152c21, []int{1} } func (m *MsgWrappedCreateValidatorResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -188,8 +108,6 @@ func (m *MsgWrappedCreateValidatorResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgWrappedCreateValidatorResponse proto.InternalMessageInfo func init() { - proto.RegisterType((*MsgAddBlsSig)(nil), "babylon.checkpointing.v1.MsgAddBlsSig") - proto.RegisterType((*MsgAddBlsSigResponse)(nil), "babylon.checkpointing.v1.MsgAddBlsSigResponse") proto.RegisterType((*MsgWrappedCreateValidator)(nil), "babylon.checkpointing.v1.MsgWrappedCreateValidator") proto.RegisterType((*MsgWrappedCreateValidatorResponse)(nil), "babylon.checkpointing.v1.MsgWrappedCreateValidatorResponse") } @@ -197,35 +115,30 @@ func init() { func init() { proto.RegisterFile("babylon/checkpointing/v1/tx.proto", fileDescriptor_6b16c54750152c21) } var fileDescriptor_6b16c54750152c21 = []byte{ - // 447 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0x4f, 0x6b, 0xd4, 0x40, - 0x18, 0xc6, 0x33, 0x16, 0x56, 0x3b, 0x7a, 0x1a, 0x96, 0x92, 0xe6, 0x90, 0xdd, 0xae, 0x50, 0xaa, - 0xe0, 0xc4, 0x6e, 0xf1, 0xa0, 0x9e, 0xba, 0x1e, 0x25, 0x08, 0x29, 0x28, 0x88, 0x10, 0x26, 0xc9, - 0x30, 0x3b, 0xe4, 0xcf, 0x84, 0xbc, 0xe3, 0xd2, 0x7c, 0x00, 0x41, 0x3c, 0xf9, 0x11, 0xfa, 0x21, - 0x04, 0xbf, 0x82, 0xc7, 0xe2, 0xc9, 0xa3, 0xec, 0x5e, 0xfc, 0x06, 0x5e, 0x65, 0x93, 0x09, 0xbb, - 0x56, 0xa3, 0xe2, 0x29, 0x99, 0x37, 0xbf, 0xf7, 0x7d, 0x9e, 0xf7, 0x49, 0x82, 0x0f, 0x22, 0x16, - 0xd5, 0x99, 0x2a, 0xbc, 0x78, 0xce, 0xe3, 0xb4, 0x54, 0xb2, 0xd0, 0xb2, 0x10, 0xde, 0xe2, 0xd8, - 0xd3, 0xe7, 0xb4, 0xac, 0x94, 0x56, 0xc4, 0x36, 0x08, 0xfd, 0x09, 0xa1, 0x8b, 0x63, 0x67, 0x28, - 0x94, 0x50, 0x0d, 0xe4, 0xad, 0xef, 0x5a, 0xde, 0xb9, 0xd3, 0x3b, 0x72, 0x53, 0x30, 0xe8, 0x61, - 0x2f, 0x1a, 0x65, 0x10, 0xa6, 0xbc, 0x36, 0xdc, 0x28, 0x56, 0x90, 0x2b, 0xf0, 0x40, 0xb3, 0xb4, - 0x05, 0x22, 0xae, 0xd9, 0xc6, 0xa3, 0xb3, 0xdf, 0x02, 0x61, 0x6b, 0xa6, 0x3d, 0xb4, 0x8f, 0x26, - 0x6f, 0x10, 0xbe, 0xe5, 0x83, 0x38, 0x4d, 0x92, 0x59, 0x06, 0x67, 0x52, 0x90, 0xfb, 0x78, 0x00, - 0x52, 0x14, 0xbc, 0xb2, 0xd1, 0x18, 0x1d, 0xed, 0xce, 0xec, 0xcf, 0x1f, 0xee, 0x0d, 0x4d, 0xcb, - 0x69, 0x92, 0x54, 0x1c, 0xe0, 0x4c, 0x57, 0xb2, 0x10, 0x81, 0xe1, 0xc8, 0x43, 0x7c, 0x7d, 0xed, - 0x07, 0xa4, 0xb0, 0xaf, 0x8d, 0xd1, 0xd1, 0xcd, 0xe9, 0x98, 0xf6, 0x65, 0x42, 0x5b, 0x91, 0x60, - 0x10, 0x35, 0xd7, 0x47, 0x37, 0xde, 0x5e, 0x8c, 0xac, 0x6f, 0x17, 0x23, 0x6b, 0xb2, 0x87, 0x87, - 0xdb, 0x36, 0x02, 0x0e, 0xa5, 0x2a, 0x80, 0x4f, 0x3e, 0x22, 0xbc, 0xef, 0x83, 0x78, 0x51, 0xb1, - 0xb2, 0xe4, 0xc9, 0x93, 0x8a, 0x33, 0xcd, 0x9f, 0xb3, 0x4c, 0x26, 0x4c, 0xab, 0x8a, 0x4c, 0xf1, - 0x4e, 0xca, 0xeb, 0xc6, 0xe9, 0xdf, 0x64, 0x9f, 0xf2, 0x3a, 0x58, 0xc3, 0xe4, 0x15, 0x1e, 0xe6, - 0x20, 0xc2, 0xb8, 0x19, 0x15, 0x2e, 0xba, 0x59, 0xc6, 0xfb, 0x5d, 0x6a, 0x76, 0x35, 0x61, 0x52, - 0x13, 0x26, 0xf5, 0x41, 0x5c, 0x51, 0x0f, 0x48, 0xfe, 0x4b, 0x6d, 0x6b, 0xa3, 0xdb, 0xf8, 0xa0, - 0xd7, 0x78, 0xb7, 0xde, 0xf4, 0x3b, 0xc2, 0x3b, 0x3e, 0x08, 0x12, 0xe3, 0xdd, 0xcd, 0x2b, 0x38, - 0xec, 0x5f, 0x64, 0x3b, 0x23, 0x87, 0xfe, 0x1b, 0xd7, 0x89, 0x91, 0x77, 0x08, 0xef, 0xf5, 0x04, - 0x79, 0xf2, 0xc7, 0x51, 0xbf, 0x6f, 0x72, 0x1e, 0xff, 0x47, 0x53, 0x67, 0x66, 0xf6, 0xec, 0xd3, - 0xd2, 0x45, 0x97, 0x4b, 0x17, 0x7d, 0x5d, 0xba, 0xe8, 0xfd, 0xca, 0xb5, 0x2e, 0x57, 0xae, 0xf5, - 0x65, 0xe5, 0x5a, 0x2f, 0x1f, 0x08, 0xa9, 0xe7, 0xaf, 0x23, 0x1a, 0xab, 0xdc, 0x33, 0x02, 0xf1, - 0x9c, 0xc9, 0xa2, 0x3b, 0x78, 0xe7, 0x57, 0x7e, 0x08, 0x5d, 0x97, 0x1c, 0xa2, 0x41, 0xf3, 0x41, - 0x9f, 0xfc, 0x08, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xef, 0x32, 0xf7, 0xb4, 0x03, 0x00, 0x00, + // 357 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x3d, 0x4b, 0xfb, 0x40, + 0x1c, 0xc7, 0x73, 0xff, 0xf2, 0x77, 0x38, 0xb7, 0x50, 0xb4, 0x66, 0x48, 0x1f, 0x04, 0x91, 0x0e, + 0x77, 0xb4, 0xc5, 0x45, 0xb7, 0x3a, 0x4a, 0x11, 0x3a, 0x28, 0x88, 0x50, 0x2e, 0xe9, 0x71, 0x0d, + 0x79, 0xb8, 0x90, 0xdf, 0x59, 0x9a, 0x4d, 0x9c, 0xc4, 0xc9, 0xd5, 0xad, 0x2f, 0xa1, 0x2f, 0xc3, + 0xb1, 0xa3, 0xa3, 0xb4, 0x43, 0x7d, 0x19, 0xd2, 0x24, 0x45, 0xac, 0xde, 0xe2, 0x76, 0x0f, 0x9f, + 0xdf, 0xf7, 0x81, 0x3b, 0x5c, 0x77, 0x98, 0x93, 0x06, 0x32, 0xa2, 0xee, 0x88, 0xbb, 0x7e, 0x2c, + 0xbd, 0x48, 0x79, 0x91, 0xa0, 0xe3, 0x16, 0x55, 0x13, 0x12, 0x27, 0x52, 0x49, 0xb3, 0x52, 0x20, + 0xe4, 0x1b, 0x42, 0xc6, 0x2d, 0xab, 0x2c, 0xa4, 0x90, 0x19, 0x44, 0xd7, 0xab, 0x9c, 0xb7, 0x8e, + 0xb4, 0x92, 0x4e, 0x00, 0x03, 0x9f, 0xa7, 0x05, 0x57, 0x75, 0x25, 0x84, 0x12, 0x28, 0x28, 0xe6, + 0xe7, 0x80, 0xc3, 0x15, 0xfb, 0x32, 0xb6, 0xf6, 0x0b, 0x20, 0x84, 0x6c, 0x3a, 0x04, 0x91, 0x5f, + 0x34, 0xe6, 0x08, 0x1f, 0xf4, 0x40, 0x5c, 0x27, 0x2c, 0x8e, 0xf9, 0xf0, 0x3c, 0xe1, 0x4c, 0xf1, + 0x2b, 0x16, 0x78, 0x43, 0xa6, 0x64, 0x62, 0xb6, 0x71, 0xc9, 0xe7, 0x69, 0x05, 0xd5, 0xd0, 0xf1, + 0x6e, 0xbb, 0x46, 0x74, 0xe9, 0x49, 0x37, 0x80, 0x0b, 0x9e, 0xf6, 0xd7, 0xb0, 0x79, 0x8b, 0xcb, + 0x21, 0x88, 0x81, 0x9b, 0x49, 0x0d, 0xc6, 0x1b, 0xad, 0xca, 0xbf, 0x4c, 0xa4, 0x49, 0xf2, 0x24, + 0xa4, 0x88, 0x4a, 0x8a, 0xa8, 0xa4, 0x07, 0x62, 0xcb, 0xbd, 0x6f, 0x86, 0x3f, 0xce, 0x4e, 0xeb, + 0x8f, 0xd3, 0xaa, 0xf1, 0x31, 0xad, 0x1a, 0x0f, 0xab, 0x59, 0xf3, 0x57, 0xa3, 0xc6, 0x21, 0xae, + 0x6b, 0x1b, 0xf5, 0x39, 0xc4, 0x32, 0x02, 0xde, 0x7e, 0x41, 0xb8, 0xd4, 0x03, 0x61, 0x3e, 0x21, + 0xbc, 0xa7, 0x29, 0xdf, 0xd1, 0xf7, 0xd5, 0xea, 0x5b, 0x67, 0x7f, 0x18, 0xda, 0x84, 0xb2, 0xfe, + 0xdf, 0xaf, 0x66, 0x4d, 0xd4, 0xbd, 0x7c, 0x5d, 0xd8, 0x68, 0xbe, 0xb0, 0xd1, 0xfb, 0xc2, 0x46, + 0xcf, 0x4b, 0xdb, 0x98, 0x2f, 0x6d, 0xe3, 0x6d, 0x69, 0x1b, 0x37, 0x27, 0xc2, 0x53, 0xa3, 0x3b, + 0x87, 0xb8, 0x32, 0xa4, 0x85, 0x8f, 0x3b, 0x62, 0x5e, 0xb4, 0xd9, 0xd0, 0xc9, 0xd6, 0x4f, 0x51, + 0x69, 0xcc, 0xc1, 0xd9, 0xc9, 0xde, 0xba, 0xf3, 0x19, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x29, 0x9a, + 0x31, 0xa2, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -240,8 +153,6 @@ 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 { - // AddBlsSig defines a method for accumulating BLS signatures - AddBlsSig(ctx context.Context, in *MsgAddBlsSig, opts ...grpc.CallOption) (*MsgAddBlsSigResponse, error) // WrappedCreateValidator defines a method for registering a new validator WrappedCreateValidator(ctx context.Context, in *MsgWrappedCreateValidator, opts ...grpc.CallOption) (*MsgWrappedCreateValidatorResponse, error) } @@ -254,15 +165,6 @@ func NewMsgClient(cc grpc1.ClientConn) MsgClient { return &msgClient{cc} } -func (c *msgClient) AddBlsSig(ctx context.Context, in *MsgAddBlsSig, opts ...grpc.CallOption) (*MsgAddBlsSigResponse, error) { - out := new(MsgAddBlsSigResponse) - err := c.cc.Invoke(ctx, "/babylon.checkpointing.v1.Msg/AddBlsSig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *msgClient) WrappedCreateValidator(ctx context.Context, in *MsgWrappedCreateValidator, opts ...grpc.CallOption) (*MsgWrappedCreateValidatorResponse, error) { out := new(MsgWrappedCreateValidatorResponse) err := c.cc.Invoke(ctx, "/babylon.checkpointing.v1.Msg/WrappedCreateValidator", in, out, opts...) @@ -274,8 +176,6 @@ func (c *msgClient) WrappedCreateValidator(ctx context.Context, in *MsgWrappedCr // MsgServer is the server API for Msg service. type MsgServer interface { - // AddBlsSig defines a method for accumulating BLS signatures - AddBlsSig(context.Context, *MsgAddBlsSig) (*MsgAddBlsSigResponse, error) // WrappedCreateValidator defines a method for registering a new validator WrappedCreateValidator(context.Context, *MsgWrappedCreateValidator) (*MsgWrappedCreateValidatorResponse, error) } @@ -284,9 +184,6 @@ type MsgServer interface { type UnimplementedMsgServer struct { } -func (*UnimplementedMsgServer) AddBlsSig(ctx context.Context, req *MsgAddBlsSig) (*MsgAddBlsSigResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AddBlsSig not implemented") -} func (*UnimplementedMsgServer) WrappedCreateValidator(ctx context.Context, req *MsgWrappedCreateValidator) (*MsgWrappedCreateValidatorResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WrappedCreateValidator not implemented") } @@ -295,24 +192,6 @@ func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) } -func _Msg_AddBlsSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgAddBlsSig) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).AddBlsSig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/babylon.checkpointing.v1.Msg/AddBlsSig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).AddBlsSig(ctx, req.(*MsgAddBlsSig)) - } - return interceptor(ctx, in, info, handler) -} - func _Msg_WrappedCreateValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgWrappedCreateValidator) if err := dec(in); err != nil { @@ -335,10 +214,6 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "babylon.checkpointing.v1.Msg", HandlerType: (*MsgServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "AddBlsSig", - Handler: _Msg_AddBlsSig_Handler, - }, { MethodName: "WrappedCreateValidator", Handler: _Msg_WrappedCreateValidator_Handler, @@ -348,71 +223,6 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ Metadata: "babylon/checkpointing/v1/tx.proto", } -func (m *MsgAddBlsSig) 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 *MsgAddBlsSig) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgAddBlsSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.BlsSig != nil { - { - size, err := m.BlsSig.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *MsgAddBlsSigResponse) 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 *MsgAddBlsSigResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgAddBlsSigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - func (m *MsgWrappedCreateValidator) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -494,32 +304,6 @@ func encodeVarintTx(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *MsgAddBlsSig) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.BlsSig != nil { - l = m.BlsSig.Size() - n += 1 + l + sovTx(uint64(l)) - } - return n -} - -func (m *MsgAddBlsSigResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - func (m *MsgWrappedCreateValidator) Size() (n int) { if m == nil { return 0 @@ -552,174 +336,6 @@ func sovTx(x uint64) (n int) { func sozTx(x uint64) (n int) { return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *MsgAddBlsSig) 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: MsgAddBlsSig: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAddBlsSig: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BlsSig", 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 m.BlsSig == nil { - m.BlsSig = &BlsSig{} - } - if err := m.BlsSig.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 *MsgAddBlsSigResponse) 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: MsgAddBlsSigResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAddBlsSigResponse: 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 (m *MsgWrappedCreateValidator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/checkpointing/types/types.go b/x/checkpointing/types/types.go index 3f2a26b4a..49bb68f62 100644 --- a/x/checkpointing/types/types.go +++ b/x/checkpointing/types/types.go @@ -1,35 +1,38 @@ package types import ( + "context" "crypto/sha256" "encoding/hex" "errors" - "github.com/babylonchain/babylon/crypto/bls12381" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" "github.com/boljen/go-bitmap" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + txformat "github.com/babylonchain/babylon/btctxformatter" + "github.com/babylonchain/babylon/crypto/bls12381" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" ) const ( // HashSize is the size in bytes of a hash HashSize = sha256.Size - BitmapBits = 104 // 104 bits for 104 validators at top + BitmapBits = txformat.BitMapLength * 8 // 104 bits for 104 validators at top ) -type LastCommitHash []byte +type BlockHash []byte type BlsSigHash []byte type RawCkptHash []byte -func NewCheckpoint(epochNum uint64, lch LastCommitHash) *RawCheckpoint { +func NewCheckpoint(epochNum uint64, blockHash BlockHash) *RawCheckpoint { return &RawCheckpoint{ - EpochNum: epochNum, - LastCommitHash: &lch, - Bitmap: bitmap.New(BitmapBits), // 13 bytes, holding 100 validators - BlsMultiSig: nil, + EpochNum: epochNum, + BlockHash: &blockHash, + Bitmap: bitmap.New(BitmapBits), // 13 bytes, holding 100 validators + BlsMultiSig: nil, } } @@ -97,7 +100,7 @@ func (cm *RawCheckpointWithMeta) Accumulate( // accumulate voting power and update status when the threshold is reached cm.PowerSum += uint64(val.Power) - if int64(cm.PowerSum) > totalPower/3 { + if int64(cm.PowerSum)*3 > totalPower*2 { cm.Status = Sealed } @@ -110,8 +113,9 @@ func (cm *RawCheckpointWithMeta) IsMoreMatureThanStatus(status CheckpointStatus) // RecordStateUpdate appends a new state update to the raw ckpt with meta // where the time/height are captured by the current ctx -func (cm *RawCheckpointWithMeta) RecordStateUpdate(ctx sdk.Context, status CheckpointStatus) { - height, time := ctx.BlockHeight(), ctx.BlockTime() +func (cm *RawCheckpointWithMeta) RecordStateUpdate(ctx context.Context, status CheckpointStatus) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + height, time := sdkCtx.HeaderInfo().Height, sdkCtx.HeaderInfo().Time stateUpdate := &CheckpointStateUpdate{ State: status, BlockHeight: uint64(height), @@ -120,82 +124,62 @@ func (cm *RawCheckpointWithMeta) RecordStateUpdate(ctx sdk.Context, status Check cm.Lifecycle = append(cm.Lifecycle, stateUpdate) } -func NewLastCommitHashFromHex(s string) (LastCommitHash, error) { - bz, err := hex.DecodeString(s) - if err != nil { - return nil, err - } - - var lch LastCommitHash - - err = lch.Unmarshal(bz) - if err != nil { - return nil, err - } - - return lch, nil -} - -func (lch *LastCommitHash) Unmarshal(bz []byte) error { +func (bh *BlockHash) Unmarshal(bz []byte) error { if len(bz) != HashSize { - return errors.New("invalid lastCommitHash length") + return errors.New("invalid appHash length") } - *lch = bz + *bh = bz return nil } -func (lch *LastCommitHash) Size() (n int) { - if lch == nil { +func (bh *BlockHash) Size() (n int) { + if bh == nil { return 0 } - return len(*lch) + return len(*bh) } -func (lch *LastCommitHash) Equal(l LastCommitHash) bool { - return lch.String() == l.String() +func (bh *BlockHash) Equal(l BlockHash) bool { + return bh.String() == l.String() } -func (lch *LastCommitHash) String() string { - return hex.EncodeToString(*lch) +func (bh *BlockHash) String() string { + return hex.EncodeToString(*bh) } -func (lch *LastCommitHash) MustMarshal() []byte { - bz, err := lch.Marshal() +func (bh *BlockHash) MustMarshal() []byte { + bz, err := bh.Marshal() if err != nil { panic(err) } return bz } -func (lch *LastCommitHash) Marshal() ([]byte, error) { - return *lch, nil +func (bh *BlockHash) Marshal() ([]byte, error) { + return *bh, nil } -func (lch LastCommitHash) MarshalTo(data []byte) (int, error) { - copy(data, lch) +func (bh BlockHash) MarshalTo(data []byte) (int, error) { + copy(data, bh) return len(data), nil } -func (lch *LastCommitHash) ValidateBasic() error { - if lch == nil { - return errors.New("invalid lastCommitHash") +func (bh *BlockHash) ValidateBasic() error { + if bh == nil { + return errors.New("invalid block hash") } - if len(*lch) != HashSize { - return errors.New("invalid lastCommitHash") + if len(*bh) != HashSize { + return errors.New("invalid block hash") } return nil } -func RawCkptToBytes(cdc codec.BinaryCodec, ckpt *RawCheckpoint) []byte { - return cdc.MustMarshal(ckpt) -} - // ValidateBasic does sanity checks on a raw checkpoint func (ckpt RawCheckpoint) ValidateBasic() error { if ckpt.Bitmap == nil { return ErrInvalidRawCheckpoint.Wrapf("bitmap cannot be empty") } - err := ckpt.LastCommitHash.ValidateBasic() + err := ckpt.BlockHash.ValidateBasic() if err != nil { return ErrInvalidRawCheckpoint.Wrapf(err.Error()) } @@ -216,3 +200,14 @@ func BytesToCkptWithMeta(cdc codec.BinaryCodec, bz []byte) (*RawCheckpointWithMe err := cdc.Unmarshal(bz, ckptWithMeta) return ckptWithMeta, err } + +func (ve *VoteExtension) ToBLSSig() *BlsSig { + blockHash := BlockHash(ve.BlockHash) + return &BlsSig{ + EpochNum: ve.EpochNum, + BlockHash: &blockHash, + BlsSig: ve.BlsSig, + SignerAddress: ve.Signer, + ValidatorAddress: ve.ValidatorAddress, + } +} diff --git a/x/checkpointing/types/types_test.go b/x/checkpointing/types/types_test.go index d6d06a1f3..3875024c9 100644 --- a/x/checkpointing/types/types_test.go +++ b/x/checkpointing/types/types_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/client" "github.com/stretchr/testify/require" "github.com/babylonchain/babylon/testutil/datagen" @@ -19,11 +18,11 @@ func TestRawCheckpointWithMeta_Accumulate1(t *testing.T) { epochNum := uint64(2) n := 1 totalPower := int64(10) - ckptkeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil, client.Context{}) - lch := datagen.GenRandomLastCommitHash(r) - msg := types.GetSignBytes(epochNum, lch) + ckptkeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil) + blockHash := datagen.GenRandomBlockHash(r) + msg := types.GetSignBytes(epochNum, blockHash) blsPubkeys, blsSigs := datagen.GenRandomPubkeysAndSigs(n, msg) - ckpt, err := ckptkeeper.BuildRawCheckpoint(ctx, epochNum, lch) + ckpt, err := ckptkeeper.BuildRawCheckpoint(ctx, epochNum, blockHash) require.NoError(t, err) valSet := datagen.GenRandomValSet(n) err = ckpt.Accumulate(valSet, valSet[0].Addr, blsPubkeys[0], blsSigs[0], totalPower) @@ -42,24 +41,23 @@ func TestRawCheckpointWithMeta_Accumulate4(t *testing.T) { epochNum := uint64(2) n := 4 totalPower := int64(10) * int64(n) - ckptkeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil, client.Context{}) - lch := datagen.GenRandomLastCommitHash(r) - msg := types.GetSignBytes(epochNum, lch) + ckptkeeper, ctx, _ := testkeeper.CheckpointingKeeper(t, nil, nil) + blockHash := datagen.GenRandomBlockHash(r) + msg := types.GetSignBytes(epochNum, blockHash) blsPubkeys, blsSigs := datagen.GenRandomPubkeysAndSigs(n, msg) - ckpt, err := ckptkeeper.BuildRawCheckpoint(ctx, epochNum, lch) + ckpt, err := ckptkeeper.BuildRawCheckpoint(ctx, epochNum, blockHash) require.NoError(t, err) valSet := datagen.GenRandomValSet(n) for i := 0; i < n; i++ { err = ckpt.Accumulate(valSet, valSet[i].Addr, blsPubkeys[i], blsSigs[i], totalPower) - if i == 0 { + if i <= 1 { require.NoError(t, err) require.Equal(t, types.Accumulating, ckpt.Status) } - if i == 1 { - require.NoError(t, err) + if i == 2 { require.Equal(t, types.Sealed, ckpt.Status) } - if i >= 2 { + if i == 3 { require.ErrorIs(t, err, types.ErrCkptNotAccumulating) require.Equal(t, types.Sealed, ckpt.Status) } diff --git a/x/checkpointing/types/utils.go b/x/checkpointing/types/utils.go index 54b8b9dfd..faef788dd 100644 --- a/x/checkpointing/types/utils.go +++ b/x/checkpointing/types/utils.go @@ -14,7 +14,7 @@ import ( func (m BlsSig) Hash() BlsSigHash { fields := [][]byte{ sdk.Uint64ToBigEndian(m.EpochNum), - m.LastCommitHash.MustMarshal(), + m.BlockHash.MustMarshal(), m.BlsSig.MustMarshal(), []byte(m.SignerAddress), } @@ -24,7 +24,7 @@ func (m BlsSig) Hash() BlsSigHash { func (m RawCheckpoint) Hash() RawCkptHash { fields := [][]byte{ sdk.Uint64ToBigEndian(m.EpochNum), - m.LastCommitHash.MustMarshal(), + m.BlockHash.MustMarshal(), m.BlsMultiSig.MustMarshal(), m.Bitmap, } @@ -36,9 +36,9 @@ func (m RawCheckpoint) HashStr() string { } // SignedMsg is the message corresponding to the BLS sig in this raw checkpoint -// Its value should be (epoch_number || last_commit_hash) +// Its value should be (epoch_number || app_hash) func (m RawCheckpoint) SignedMsg() []byte { - return append(sdk.Uint64ToBigEndian(m.EpochNum), *m.LastCommitHash...) + return append(sdk.Uint64ToBigEndian(m.EpochNum), *m.BlockHash...) } func hash(fields [][]byte) []byte { @@ -78,8 +78,8 @@ func FromBTCCkptBytesToRawCkpt(btcCkptBytes []byte) (*RawCheckpoint, error) { } func FromBTCCkptToRawCkpt(btcCkpt *btctxformatter.RawBtcCheckpoint) (*RawCheckpoint, error) { - var lch LastCommitHash - err := lch.Unmarshal(btcCkpt.LastCommitHash) + var blockHash BlockHash + err := blockHash.Unmarshal(btcCkpt.BlockHash) if err != nil { return nil, err } @@ -89,17 +89,17 @@ func FromBTCCkptToRawCkpt(btcCkpt *btctxformatter.RawBtcCheckpoint) (*RawCheckpo return nil, err } rawCheckpoint := &RawCheckpoint{ - EpochNum: btcCkpt.Epoch, - LastCommitHash: &lch, - Bitmap: btcCkpt.BitMap, - BlsMultiSig: &blsSig, + EpochNum: btcCkpt.Epoch, + BlockHash: &blockHash, + Bitmap: btcCkpt.BitMap, + BlsMultiSig: &blsSig, } return rawCheckpoint, nil } func FromRawCkptToBTCCkpt(rawCkpt *RawCheckpoint, address []byte) (*btctxformatter.RawBtcCheckpoint, error) { - lchBytes, err := rawCkpt.LastCommitHash.Marshal() + appHashBytes, err := rawCkpt.BlockHash.Marshal() if err != nil { return nil, err } @@ -110,7 +110,7 @@ func FromRawCkptToBTCCkpt(rawCkpt *RawCheckpoint, address []byte) (*btctxformatt btcCkpt := &btctxformatter.RawBtcCheckpoint{ Epoch: rawCkpt.EpochNum, - LastCommitHash: lchBytes, + BlockHash: appHashBytes, BitMap: rawCkpt.Bitmap, SubmitterAddress: address, BlsSig: blsSigBytes, diff --git a/x/checkpointing/types/val_bls_set.go b/x/checkpointing/types/val_bls_set.go index 0eac54da1..8db7764e5 100644 --- a/x/checkpointing/types/val_bls_set.go +++ b/x/checkpointing/types/val_bls_set.go @@ -1,7 +1,7 @@ package types import ( - fmt "fmt" + "fmt" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/boljen/go-bitmap" diff --git a/x/checkpointing/vote_ext.go b/x/checkpointing/vote_ext.go new file mode 100644 index 000000000..749714124 --- /dev/null +++ b/x/checkpointing/vote_ext.go @@ -0,0 +1,154 @@ +package checkpointing + +import ( + "fmt" + + "cosmossdk.io/log" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonchain/babylon/x/checkpointing/keeper" + ckpttypes "github.com/babylonchain/babylon/x/checkpointing/types" +) + +// VoteExtensionHandler defines a BLS-based vote extension handlers for Babylon. +type VoteExtensionHandler struct { + logger log.Logger + ckptKeeper *keeper.Keeper + valStore baseapp.ValidatorStore +} + +func NewVoteExtensionHandler(logger log.Logger, ckptKeeper *keeper.Keeper) *VoteExtensionHandler { + return &VoteExtensionHandler{logger: logger, ckptKeeper: ckptKeeper, valStore: ckptKeeper} +} + +func (h *VoteExtensionHandler) SetHandlers(bApp *baseapp.BaseApp) { + bApp.SetExtendVoteHandler(h.ExtendVote()) + bApp.SetVerifyVoteExtensionHandler(h.VerifyVoteExtension()) +} + +// ExtendVote sends a BLS signature as a vote extension +// the signature is signed over the hash of the last +// block of the current epoch +func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler { + return func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) { + k := h.ckptKeeper + // the returned response MUST not be nil + emptyRes := &abci.ResponseExtendVote{VoteExtension: []byte{}} + + epoch := k.GetEpoch(ctx) + // BLS vote extension is only applied at the last block of the current epoch + if !epoch.IsLastBlockByHeight(req.Height) { + return emptyRes, nil + } + + // 1. check if itself is the validator as the BLS sig is only signed + // when the node itself is a validator + signer := k.GetBLSSignerAddress() + curValSet := k.GetValidatorSet(ctx, epoch.EpochNumber) + _, _, err := curValSet.FindValidatorWithIndex(signer) + if err != nil { + // not being a validator is not an error + h.logger.Info("the BLS signer is not in the validator set", "signer_address", signer.String()) + return emptyRes, nil + } + + // 2. sign BLS signature + blsSig, err := k.SignBLS(epoch.EpochNumber, req.Hash) + if err != nil { + // the returned error will lead to panic + return emptyRes, fmt.Errorf("failed to sign BLS signature at epoch %v, height %v", + epoch.EpochNumber, req.Height) + } + + // 3. build vote extension + ve := &ckpttypes.VoteExtension{ + Signer: signer.String(), + ValidatorAddress: k.GetValidatorAddress().String(), + BlockHash: req.Hash, + EpochNum: epoch.EpochNumber, + Height: uint64(req.Height), + BlsSig: &blsSig, + } + bz, err := ve.Marshal() + if err != nil { + return emptyRes, fmt.Errorf("failed to encode vote extension: %w", err) + } + + h.logger.Info("successfully sent BLS signature in vote extension", + "epoch", epoch.EpochNumber, "height", req.Height) + + return &abci.ResponseExtendVote{VoteExtension: bz}, nil + } +} + +// VerifyVoteExtension verifies the BLS sig within the vote extension +func (h *VoteExtensionHandler) VerifyVoteExtension() sdk.VerifyVoteExtensionHandler { + return func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) { + k := h.ckptKeeper + resAccept := &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT} + resReject := &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT} + + epoch := k.GetEpoch(ctx) + // BLS vote extension is only applied at the last block of the current epoch + if !epoch.IsLastBlockByHeight(req.Height) { + return resAccept, nil + } + + if len(req.VoteExtension) == 0 { + h.logger.Error("received empty vote extension", "height", req.Height) + return resReject, nil + } + + var ve ckpttypes.VoteExtension + if err := ve.Unmarshal(req.VoteExtension); err != nil { + h.logger.Error("failed to unmarshal vote extension", "err", err, "height", req.Height) + return resReject, nil + } + + // 1. verify epoch number + if epoch.EpochNumber != ve.EpochNum { + h.logger.Error("invalid epoch number in the vote extension", + "want", epoch.EpochNumber, "got", ve.EpochNum) + return resReject, nil + } + + // 2. ensure the validator address in the BLS sig matches the signer of the vote extension + // this prevents validators use valid BLS from another validator + blsSig := ve.ToBLSSig() + extensionSigner := sdk.ValAddress(req.ValidatorAddress).String() + if extensionSigner != blsSig.ValidatorAddress { + h.logger.Error("the vote extension signer does not match the BLS signature signer", + "extension signer", extensionSigner, "BLS signer", blsSig.SignerAddress) + return resReject, nil + } + + // 3. verify signing hash + if !blsSig.BlockHash.Equal(req.Hash) { + // processed BlsSig message is for invalid last commit hash + h.logger.Error("in valid block ID in BLS sig", "want", req.Hash, "got", blsSig.BlockHash) + return resReject, nil + } + + // 4. verify the validity of the BLS signature + if err := k.VerifyBLSSig(ctx, blsSig); err != nil { + // Note: reject this vote extension as BLS is invalid + // this will also reject the corresponding PreCommit vote + h.logger.Error("invalid BLS sig in vote extension", + "err", err, + "height", req.Height, + "epoch", epoch.EpochNumber, + ) + return resReject, nil + } + + h.logger.Info("successfully verified vote extension", + "signer_address", ve.Signer, + "height", req.Height, + "epoch", epoch.EpochNumber, + ) + + return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil + } +} diff --git a/x/checkpointing/vote_ext_test.go b/x/checkpointing/vote_ext_test.go new file mode 100644 index 000000000..115adc4e4 --- /dev/null +++ b/x/checkpointing/vote_ext_test.go @@ -0,0 +1,101 @@ +package checkpointing_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + "github.com/babylonchain/babylon/x/checkpointing/types" +) + +// FuzzAddBLSSigVoteExtension_MultipleVals tests adding BLS signatures via VoteExtension +// with multiple validators +func FuzzAddBLSSigVoteExtension_MultipleVals(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 4) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ek := helper.App.EpochingKeeper + ck := helper.App.CheckpointingKeeper + + epoch := ek.GetEpoch(helper.Ctx) + require.Equal(t, uint64(1), epoch.EpochNumber) + + // go to block 11, ensure the checkpoint is finalized + interval := ek.GetParams(helper.Ctx).EpochInterval + for i := uint64(0); i < interval; i++ { + _, err := helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) + } + + epoch = ek.GetEpoch(helper.Ctx) + require.Equal(t, uint64(2), epoch.EpochNumber) + + ckpt, err := ck.GetRawCheckpoint(helper.Ctx, epoch.EpochNumber-1) + require.NoError(t, err) + require.Equal(t, types.Sealed, ckpt.Status) + }) +} + +// FuzzAddBLSSigVoteExtension_InsufficientVotingPower tests adding BLS signatures +// with insufficient voting power +func FuzzAddBLSSigVoteExtension_InsufficientVotingPower(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 4) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ek := helper.App.EpochingKeeper + + epoch := ek.GetEpoch(helper.Ctx) + require.Equal(t, uint64(1), epoch.EpochNumber) + + // the number of validators is less than 2/3 if the total set + numOfValidators := datagen.RandomInt(r, 5) + 1 + genesisValSet.Keys = genesisValSet.Keys[:numOfValidators] + interval := ek.GetParams(helper.Ctx).EpochInterval + for i := uint64(0); i < interval-1; i++ { + _, err := helper.ApplyEmptyBlockWithValSet(r, genesisValSet) + if i < interval-2 { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } + }) +} + +// FuzzAddBLSSigVoteExtension_InvalidBLSSig tests adding BLS signatures +// with invalid BLS signature +func FuzzAddBLSSigVoteExtension_InvalidBLSSig(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 4) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + helper := testhelper.NewHelper(t) + ek := helper.App.EpochingKeeper + + epoch := ek.GetEpoch(helper.Ctx) + require.Equal(t, uint64(1), epoch.EpochNumber) + + interval := ek.GetParams(helper.Ctx).EpochInterval + for i := uint64(0); i < interval-1; i++ { + _, err := helper.ApplyEmptyBlockWithInvalidBLSSig(r) + if i < interval-2 { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } + }) +} diff --git a/x/epoching/README.md b/x/epoching/README.md new file mode 100644 index 000000000..e557c8df8 --- /dev/null +++ b/x/epoching/README.md @@ -0,0 +1,530 @@ +# Epoching + +Babylon implements [epoched +staking](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-039-epoched-staking.md) +to reduce and parameterize the frequency of updating the validator set in +Babylon. This reduces the frequency of Babylon sending checkpoints to Bitcoin, +thereby reducing Babylon's operation cost and its footprint on Bitcoin. + +In the epoched staking design, the blockchain is divided into epochs, each of +which contains a fixed number of consecutive blocks. Messages that affect the +validator set's stake distribution are delayed to the end of each epoch for +execution, such that the validator set remains unchanged during each epoch. The +Epoching module is responsible for implementing the epoched staking design, +including: + +- tracking the current epoch number of the blockchain; +- recording the metadata of each epoch; +- delaying the execution of messages that affect the validator set's stake + distribution to the end of each epoch; and +- finishing all unbonding requests for epochs that have a Bitcoin checkpoint + with sufficient confirmations. + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Concepts](#concepts) + - [Problem statement](#problem-statement) + - [Babylon's Epoching module design](#babylons-epoching-module-design) +- [States](#states) + - [Parameters](#parameters) + - [Epochs](#epochs) + - [Epoch message queue](#epoch-message-queue) + - [Epoch validator set](#epoch-validator-set) +- [Messages](#messages) + - [Disabling Staking module messages via AnteHandler](#disabling-staking-module-messages-via-antehandler) + - [Epoched staking messages](#epoched-staking-messages) + - [MsgUpdateParams](#msgupdateparams) +- [BeginBlocker and EndBlocker](#beginblocker-and-endblocker) + - [Disabling Staking module's EndBlocker](#disabling-staking-modules-endblocker) +- [BeginBlocker](#beginblocker) +- [EndBlocker](#endblocker) +- [Hooks](#hooks) + - [Hooks in the Epoching module](#hooks-in-the-epoching-module) + - [Bitcoin-assisted unbonding via the `AfterRawCheckpointFinalized` hook](#bitcoin-assisted-unbonding-via-the-afterrawcheckpointfinalized-hook) +- [Events](#events) +- [Queries](#queries) + +## Concepts + +### Problem statement + +In the Cosmos SDK, the validator set can change with every block, impacting +stake distribution through various staking-related actions (e.g., bond/unbond, +delegate/undelegate/redelegate, slash). This frequent updating poses challenges, +as + +1. Babylon's Bitcoin Timestamping protocol requires checkpointing the validator + set to Bitcoin upon every validator set update; +2. Bitcoin's 10-minute block interval makes checkpointing every new block + impractical; and +3. frequent validator set updates complicate the implementation of threshold + cryptography, light clients, fair leader election, and staking derivatives, + as highlighted in + [ADR-039](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-039-epoched-staking.md). + +We introduce the concept of epoching to reduce the frequency of validator set +update, thereby addressing the above challenges. In the epoching design, the +blockchain is divided into epochs, and updating the validator set once per +epoch. This approach, detailed in +[ADR-039](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-039-epoched-staking.md), +has been pursued by multiple efforts (e.g., +[here](https://github.com/cosmos/cosmos-sdk/pull/8829), +[here](https://github.com/cosmos/cosmos-sdk/pull/10132), and +[here](https://github.com/cosmos/cosmos-sdk/pull/10173)) but was not fully +implemented. Babylon has implemented the Epoching module, catering to specific +design goals such as checkpointing epochs. In addition, Babylon implements +*Bitcoin-assisted unbonding*, where unbonding requests in an epoch will be +finished when the epoch they were created in is checkpointed on Bitcoin with +sufficient confirmations. + +### Babylon's Epoching module design + +Babylon implements the Epoching module in order to reduce the frequency of +validator set updates, and thus the frequency of checkpointing to Bitcoin. +Specifically, the Epoching module is responsible for the following tasks: + +- Dividing the blockchain into epochs. +- Disabling some functionalities of the Staking module. +- Disabling messages of the Staking module. +- Delaying staking-related messages till the end of the epoch. +- Bitcoin-assisted unbonding. + +**Dividing the blockchain into epochs.** The epoching mechanism introduces the +concept of epochs. The blockchain is divided into epochs, each consisting of a +fixed number of consecutive blocks. The number of blocks in an epoch is called +epoch interval, which is a system parameter. + +**Disabling functionalities of the Staking module.** Babylon disables two +functionalities of the Staking module, namely the validator set update mechanism +and the 21-day unbonding mechanism. + +In Cosmos SDK, the Staking module handles staking-related messages and updates +the validator set upon every block. Consequently, the Staking module updates the +validator set upon every block. In order to reduce the frequency of validator +set updates to once per epoch, Babylon disables the validator set update +mechanism of the Staking module. + +In addition, the Staking module enforces the "21-day unbonding rule": unbonding +validators and delegations will become unbonded after 21 days (in the default +case). The long unbonding period aims to circumvent the [long-range +attack](https://medium.com/babylonchain-io/why-are-unbonding-periods-so-long-on-proof-of-stake-d44e863c5cb8), +at the cost of capital efficiency. Babylon departs from Cosmos SDK by employing +Bitcoin-assisted unbonding, where unbonding validators and delegations become +unbonded once the corresponding epoch has been checkpointed on Bitcoin. Babylon +disables the 21-day unbonding mechanism to this end. + +In order to disable the two functionalities, Babylon disables Staking module's +`EndBlocker` function that updates validator sets and unbonds mature validators +upon a block ends. Instead, upon an epoch that has ended, the Epoching module +will invoke the Staking module's functionality that updates the validator set. +In addition, upon an epoch that has been checkpointed to Bitcoin, the Epoching +module will invoke the Staking module's functionality that unbonds mature +validators. + +**Disabling messages of the Staking module.** In order to keep the validator set +unchanged during each epoch, the Epoching module intercepts and rejects +staking-related messages that affect the validator set's stake distribution via +the +[AnteHandler](https://docs.cosmos.network/main/learn/advanced/baseapp#antehandler), +Instead, the Epoching module defines wrapped versions of them and forwards their +unwrapped forms to the Staking module upon the end of an epoch. In the [Staking +module](https://github.com/cosmos/cosmos-sdk/blob/v0.50.3/proto/cosmos/staking/v1beta1/tx.proto), +these messages include + +- `MsgCreateValidator` for creating a new validator +- `MsgDelegate` for delegating coins from a delegator to a validator +- `MsgBeginRedelegate` for redelegating coins from a delegator and source + validator to a destination validator. +- `MsgUndelegate` for undelegating from a delegator and a validator. +- `MsgCancelUnbondingDelegation` for cancelling an unbonding delegation of a + delegator. + +The above messages affect the validator set's stake distribution. The Epoching +module implements an `AnteHandler` to reject these messages, while also +implementing wrapped versions of them together with the Checkpointing module: +`MsgWrappedCreateValidator`, `MsgWrappedDelegate`, `MsgWrappedBeginRedelegate`, +and `MsgWrappedUndelegate`. The Epoching module receives these messages at any +time, but will only process them at the end of each epoch. + +**Delaying wrapped messages to the end of epochs.** The Epoching module +maintains a message queue for each epoch. Upon each wrapped message, the +Epoching module performs basic sanity checks, then enqueues the message to the +message queue. When the epoch ends, the Epoching module will forward queued +messages to the Staking module. Consequently, the Staking module receives and +handles staking-related messages, and performs validator set updates. + +**Bitcoin-assisted Unbonding.** Babylon implements the Bitcoin-assisted +unbonding mechanism by invoking the Staking module upon a checkpointed epoch . +Specifically, the Staking module's `BlockValidatorUpdates` +[function](https://github.com/cosmos/cosmos-sdk/blob/7e6948f50cd4838a0161838a099f74e0b5b0213c/x/staking/keeper/val_state_change.go#L36-L102) +is responsible for identifying and unbonding mature validators and delegations +that have been unbonding for 21 days, and is invoked upon every block. Babylon +has disabled the invocation of `BlockValidatorUpdates` per block, and implements +the state management for epochs. When an epoch has a checkpoint that is +sufficiently deep in Bitcoin, the Epoching module will invoke +`BlockValidatorUpdates` to finish all unbonding validators and delegations. + +## States + +The Epoching module maintains the following KV stores. + +### Parameters + +The [parameter storage](./keeper/params.go) maintains the Epoching module's +parameters. The Epoching module's parameters are represented as a `Params` +[object](../../proto/babylon/epoching/v1/params.proto) defined as follows: + +```protobuf +// Params defines the parameters for the module. +message Params { + option (gogoproto.equal) = true; + + // epoch_interval is the number of consecutive blocks to form an epoch + uint64 epoch_interval = 1 + [ (gogoproto.moretags) = "yaml:\"epoch_interval\"" ]; +} +``` + +### Epochs + +The [epoch storage](./keeper/params.go) maintains the metadata of each epoch. +The key is the epoch number, and the value is an `Epoch` +[object](../../proto/babylon/epoching/v1/epoching.proto) representing the epoch +metadata. + +```protobuf +// Epoch is a structure that contains the metadata of an epoch +message Epoch { + // epoch_number is the number of this epoch + uint64 epoch_number = 1; + // current_epoch_interval is the epoch interval at the time of this epoch + uint64 current_epoch_interval = 2; + // first_block_height is the height of the first block in this epoch + uint64 first_block_height = 3; + // last_block_time is the time of the last block in this epoch. + // Babylon needs to remember the last header's time of each epoch to complete + // unbonding validators/delegations when a previous epoch's checkpoint is + // finalised. The last_block_time field is nil in the epoch's beginning, and + // is set upon the end of this epoch. + google.protobuf.Timestamp last_block_time = 4 [ (gogoproto.stdtime) = true ]; + // app_hash_root is the Merkle root of all AppHashs in this epoch + // It will be used for proving a block is in an epoch + bytes app_hash_root = 5; + // sealer is the last block of the sealed epoch + // sealer_app_hash points to the sealer but stored in the 1st header + // of the next epoch + bytes sealer_app_hash = 6; + // sealer_block_hash is the hash of the sealer + // the validator set has generated a BLS multisig on the hash, + // i.e., hash of the last block in the epoch + bytes sealer_block_hash = 7; +} +``` + +### Epoch message queue + +The Epoching module implements a message queue to delay the execution of +messages that affect the validator set's stake distribution to the end of each +epoch. This ensures that during an epoch, the validator set's stake distribution +will remain unchanged, except for slashed validators. The [epoch message queue +storage](./keeper/epoch_msg_queue.go) maintains the queue of these +staking-related messages. The key is the epoch number concatenated with the +index of the queued message, and the value is a `QueuedMessage` +[object](../../proto/babylon/epoching/v1/epoching.proto) representing this +queued message. + +```protobuf +// QueuedMessage is a message that can change the validator set and is delayed +// to the end of an epoch +message QueuedMessage { + // tx_id is the ID of the tx that contains the message + bytes tx_id = 1; + // msg_id is the original message ID, i.e., hash of the marshaled message + bytes msg_id = 2; + // block_height is the height when this msg is submitted to Babylon + uint64 block_height = 3; + // block_time is the timestamp when this msg is submitted to Babylon + google.protobuf.Timestamp block_time = 4 [ (gogoproto.stdtime) = true ]; + // msg is the actual message that is sent by a user and is queued by the + // Epoching module + oneof msg { + cosmos.staking.v1beta1.MsgCreateValidator msg_create_validator = 5; + cosmos.staking.v1beta1.MsgDelegate msg_delegate = 6; + cosmos.staking.v1beta1.MsgUndelegate msg_undelegate = 7; + cosmos.staking.v1beta1.MsgBeginRedelegate msg_begin_redelegate = 8; + cosmos.staking.v1beta1.MsgCancelUnbondingDelegation msg_cancel_unbonding_delegation = 9; + } +} +``` + +In the Cosmos SDK, the `MsgCreateValidator`, `MsgDelegate`, `MsgUndelegate`, +`MsgBeginRedelegate`, `MsgCancelUnbondingDelegation` messages of the Staking +module might affect the validator set, and are thus wrapped into `QueuedMessage` +objects. Their execution is delayed to the end of an epoch for execution. + +### Epoch validator set + +The [epoch validator set storage](./keeper/epoch_val_set.go) maintains the +validator set at the beginning of each epoch. The validator set will remain the +same throughout the epoch, unless some validators get slashed during this epoch. +The key is the epoch number concatenated with the validator's address, and the +value is this validator's voting power (in `sdk.Int`) at this epoch. + +## Messages + +The Epoching module implements the epoched staking mechanism by using an +[AnteHandler](https://docs.cosmos.network/main/learn/advanced/baseapp#antehandler) +to intercept messages that affect the validator set's stake distribution, and +implements the messages' epoched counterparts. + +### Disabling Staking module messages via AnteHandler + +In Cosmos SDK, the +[AnteHandler](https://docs.cosmos.network/main/learn/advanced/baseapp#antehandler) +is a component responsible for pre-processing transactions. It functions prior +to the execution of transaction logic, performing crucial tasks such as +validating signatures, ensuring sufficient account funds for transaction fees, +and setting up the necessary context for transaction processing. This offers +flexibility in the sense that developers can customize AnteHandlers to suit the +specific needs and rules of their applications. + +The Epoching module implements an +[AnteHandler](./keeper/drop_validator_msg_decorator.go) +`DropValidatorMsgDecorator` in order to intercept messages that affect the +validator set's stake distribution in Cosmos SDK's Staking module. The messages +include `MsgCreateValidator`, `MsgDelegate`, `MsgUndelegate`, +`MsgBeginRedelegate`, `MsgCancelUnbondingDelegation`. + +### Epoched staking messages + +The epoched staking messages in the Epoching module are defined at +[proto/babylon/epoching/v1/tx.proto](../../proto/babylon/epoching/v1/tx.proto). +They are simply wrappers of the corresponding messages in Cosmos SDK's Staking +module. + +```proto +// MsgWrappedDelegate is the message for delegating stakes +message MsgWrappedDelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; + + cosmos.staking.v1beta1.MsgDelegate msg = 1; +} +// MsgWrappedUndelegate is the message for undelegating stakes +message MsgWrappedUndelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; + + cosmos.staking.v1beta1.MsgUndelegate msg = 1; +} +// MsgWrappedDelegate is the message for moving bonded stakes from a +// validator to another validator +message MsgWrappedBeginRedelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; + + cosmos.staking.v1beta1.MsgBeginRedelegate msg = 1; +} +// MsgWrappedCancelUnbondingDelegation is the message for cancelling +// an unbonding delegation +message MsgWrappedCancelUnbondingDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "msg"; + + cosmos.staking.v1beta1.MsgCancelUnbondingDelegation msg = 1; +} +``` + +The handlers of the epoched staking messages in the Epoching module are defined +at [x/epoching/keeper/msg_server.go](./keeper/msg_server.go). Each handler +performs the same [verification +logics](https://github.com/cosmos/cosmos-sdk/blob/v0.50.3/x/staking/keeper/msg_server.go) +of the corresponding message as the ones performed by the Cosmos SDK's Staking +module, and then inserts the message to the epoch message queue storage. + +### MsgUpdateParams + +The `MsgUpdateParams` message is used for updating the module parameters for the +Epoching module. It can only be executed via a govenance proposal. + +```protobuf +// MsgUpdateParams defines a message for updating Epoching 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 parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} +``` + +## BeginBlocker and EndBlocker + +Babylon disables the Staking module's EndBlocker to avoid validator set updates +upon each block. The Epoching module implements `BeginBlocker` to initialize an +epoch upon the beginning of an epoch, and implements `EndBlocker` to execute all +messages and update the validator set upon the end of an epoch. + +### Disabling Staking module's EndBlocker + +Cosmos SDK's Staking module [updates the validator +set](https://github.com/cosmos/cosmos-sdk/blob/v0.50.3/x/staking/keeper/abci.go#L23C1-L24C1) +upon `EndBlocker` of every block. In order to implement the epoching mechanism, +Babylon disables the Staking module's `EndBlocker` [as +follows](../../app/app.go). + +```go +// Babylon does not want EndBlock processing in staking +app.ModuleManager.OrderEndBlockers = append(app.ModuleManager.OrderEndBlockers[:2], app.ModuleManager.OrderEndBlockers[2+1:]...) // remove stakingtypes.ModuleName +``` + +## BeginBlocker + +Upon `BeginBlocker`, the Epoching module of each Babylon node will [execute the +following](./abci.go): + +1. If at the first block of the next epoch, then do the following: + 1. Enter a new epoch, i.e., create a new `Epoch` object and save it to the + epoch metadata storage. + 2. Record the current `AppHash` as the *sealer Apphash* for the previous + epoch. The entire `AppState` till the end of the last epoch commits to + this `AppHash`, hence the name "sealer AppHash". + 3. Initialize the epoch message queue for the current epoch. + 4. Save the current validator set to the epoch validator set storage. + 5. Trigger hooks and emit events that the chain has entered a new epoch. +2. If at the last block of the current epoch, then record the current + `BlockHash` as the *sealer BlockHash* for the current epoch. The entire + blockchain so far commits to this `BlockHash` via a hash chain, hence the + name "sealer BlockHash". + +## EndBlocker + +Upon `EndBlocker`, the Epoching module of each Babylon node will [execute the +following](./abci.go) *if at the last block of the current epoch*: + +1. Get all queued messages of this epoch in the epoch message queue storage. +2. Forward each of the queued messages to the corresponding message handler in + the Staking module. +3. Emit events about the execution results of the messages. +4. Invoke the Staking module to update the validator set. +5. Trigger hooks and emit events that the chain has ended the current epoch. + +## Hooks + +The Epoching module implements a set of hooks to notify other modules about +certain events, and utilizes the `AfterRawCheckpointFinalized` +[hook](../checkpointing/types/hooks.go) in the Checkpointing module for +Bitcoin-assisted unbonding. + +### Hooks in the Epoching module + +```go +// EpochingHooks event hooks for epoching validator object (noalias) +type EpochingHooks interface { + AfterEpochBegins(ctx context.Context, epoch uint64) // Must be called after an epoch begins + AfterEpochEnds(ctx context.Context, epoch uint64) // Must be called after an epoch ends + BeforeSlashThreshold(ctx context.Context, valSet ValidatorSet) // Must be called before a certain threshold (1/3 or 2/3) of validators are slashed in a single epoch +} +``` + +### Bitcoin-assisted unbonding via the `AfterRawCheckpointFinalized` hook + +The Epoching module subscribes to the Checkpointing module's +`AfterRawCheckpointFinalized` [hook](../checkpointing/types/hooks.go) for +Bitcoin-assisted unbonding. The `AfterRawCheckpointFinalized` hook is triggered +upon a checkpoint becoming *finalized*, i.e., Bitcoin transactions of the +checkpoint become `w`-deep in Bitcoin's canonical chain, where `w` is the +`checkpoint_finalization_timeout` +[parameter](../../proto/babylon/btccheckpoint/v1/params.proto) in the +BTCCheckpoint module. Upon `AfterRawCheckpointFinalized`, the Epoching module +will finish all unbonding validators and delegations till the epoch associated +with the finalized checkpoint, including [the following](./keeper/hooks.go): + +1. Find the metadata `Epoch` of the epoch associated with the finalized + checkpoint. +2. Find the timestamp of the last block of this epoch from `Epoch`. +3. Notify the Staking module to finish all unbonding validators and delegations + before this timestamp. + +## Events + +The Epoching module defines a set of events about the state updates of epochs, +validators, and delegations. + +```protobuf +// EventBeginEpoch is the event emitted when an epoch has started +message EventBeginEpoch { uint64 epoch_number = 1; } +// EventEndEpoch is the event emitted when an epoch has ended +message EventEndEpoch { uint64 epoch_number = 1; } +// EventHandleQueuedMsg is the event emitted when a queued message has been handled +message EventHandleQueuedMsg { + string original_event_type = 1; + uint64 epoch_number = 2; + uint64 height = 3; + bytes tx_id = 4; + bytes msg_id = 5; + repeated bytes original_attributes = 6 + [ (gogoproto.customtype) = + "github.com/cometbft/cometbft/abci/types.EventAttribute" ]; + string error = 7; +} +// EventSlashThreshold is the event emitted when a set of validators have been slashed +message EventSlashThreshold { + int64 slashed_voting_power = 1; + int64 total_voting_power = 2; + repeated bytes slashed_validators = 3; +} +// EventWrappedDelegate is the event emitted when a MsgWrappedDelegate has been queued +message EventWrappedDelegate { + string delegator_address = 1; + string validator_address = 2; + uint64 amount = 3; + string denom = 4; + uint64 epoch_boundary = 5; +} + +// EventWrappedUndelegate is the event emitted when a MsgWrappedUndelegate has been queued +message EventWrappedUndelegate { + string delegator_address = 1; + string validator_address = 2; + uint64 amount = 3; + string denom = 4; + uint64 epoch_boundary = 5; +} +// EventWrappedBeginRedelegate is the event emitted when a MsgWrappedBeginRedelegate has been queued +message EventWrappedBeginRedelegate { + string delegator_address = 1; + string source_validator_address = 2; + string destination_validator_address = 3; + uint64 amount = 4; + string denom = 5; + uint64 epoch_boundary = 6; +} +// EventWrappedCancelUnbondingDelegation is the event emitted when a MsgWrappedCancelUnbondingDelegation has been queued +message EventWrappedCancelUnbondingDelegation { + string delegator_address = 1; + string validator_address = 2; + uint64 amount = 3; + int64 creation_height = 4; + uint64 epoch_boundary = 5; +} +``` + +## Queries + +The Epoching module provides a set of queries about epochs, validators and +delegations, listed at +[docs.babylonchain.io](https://docs.babylonchain.io/docs/developer-guides/grpcrestapi#tag/Epoching). + diff --git a/x/epoching/abci.go b/x/epoching/abci.go index a41acd104..eecf14390 100644 --- a/x/epoching/abci.go +++ b/x/epoching/abci.go @@ -1,6 +1,7 @@ package epoching import ( + "context" "fmt" "time" @@ -14,7 +15,7 @@ import ( // BeginBlocker is called at the beginning of every block. // Upon each BeginBlock, -// - record the current AppHash +// - record the current BlockHash // - if reaching the epoch beginning, then // - increment epoch number // - trigger AfterEpochBegins hook @@ -24,9 +25,10 @@ import ( // - record the sealer header for the previous epoch // // NOTE: we follow Cosmos SDK's slashing/evidence modules for MVP. No need to modify them at the moment. -func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) { +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + sdkCtx := sdk.UnwrapSDKContext(ctx) // record the current AppHash k.RecordAppHash(ctx) @@ -36,6 +38,9 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) if epoch.IsFirstBlockOfNextEpoch(ctx) { // increase epoch number incEpoch := k.IncEpoch(ctx) + // record the AppHash referencing + // the last block of the previous epoch + k.RecordSealerAppHashForPrevEpoch(ctx) // init the msg queue of this new epoch k.InitMsgQueue(ctx) // init the slashed voting power of this new epoch @@ -45,19 +50,22 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) // trigger AfterEpochBegins hook k.AfterEpochBegins(ctx, incEpoch.EpochNumber) // emit BeginEpoch event - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventBeginEpoch{ EpochNumber: incEpoch.EpochNumber, }, ) if err != nil { - panic(err) + return err } } - if epoch.IsSecondBlock(ctx) { - k.RecordSealerHeaderForPrevEpoch(ctx) + if epoch.IsLastBlock(ctx) { + // record the block hash of the last block + // of the epoch to be sealed + k.RecordSealerBlockHashForEpoch(ctx) } + return nil } // EndBlocker is called at the end of every block. @@ -66,9 +74,10 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) // - trigger AfterEpochEnds hook // - emit EndEpoch event // NOTE: The epoching module is not responsible for checkpoint-assisted unbonding (unbonding -> unbonded). Instead, it wraps the staking module and exposes interfaces to the checkpointing module. The checkpointing module will do the actual checkpoint-assisted unbonding upon each EndBlock. -func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { +func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + sdkCtx := sdk.UnwrapSDKContext(ctx) validatorSetUpdate := []abci.ValidatorUpdate{} // if reaching an epoch boundary, then @@ -76,7 +85,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { if epoch.IsLastBlock(ctx) { // finalise this epoch, i.e., record the current header and the Merkle root of all AppHashs in this epoch if err := k.RecordLastHeaderAndAppHashRoot(ctx); err != nil { - panic(err) + return nil, err } // get all msgs in the msg queue queuedMsgs := k.GetCurrentEpochMsgs(ctx) @@ -89,7 +98,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { // honest validators will have consistent execution results on the queued messages if err != nil { // emit an event signalling the failed execution - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventHandleQueuedMsg{ EpochNumber: epoch.EpochNumber, Height: msg.BlockHeight, @@ -99,14 +108,14 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { }, ) if err != nil { - panic(err) + return nil, err } // skip this failed msg continue } // for each event, emit an wrapped event EventTypeHandleQueuedMsg, which attaches the original attributes plus the original event type, the epoch number, txid and msgid to the event here for _, event := range res.Events { - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventHandleQueuedMsg{ OriginalEventType: event.Type, EpochNumber: epoch.EpochNumber, @@ -116,27 +125,27 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { }, ) if err != nil { - panic(err) + return nil, err } } } // update validator set validatorSetUpdate = k.ApplyAndReturnValidatorSetUpdates(ctx) - ctx.Logger().Info(fmt.Sprintf("Epoching: validator set update of epoch %d: %v", epoch.EpochNumber, validatorSetUpdate)) + sdkCtx.Logger().Info(fmt.Sprintf("Epoching: validator set update of epoch %d: %v", epoch.EpochNumber, validatorSetUpdate)) // trigger AfterEpochEnds hook k.AfterEpochEnds(ctx, epoch.EpochNumber) // emit EndEpoch event - err := ctx.EventManager().EmitTypedEvent( + err := sdkCtx.EventManager().EmitTypedEvent( &types.EventEndEpoch{ EpochNumber: epoch.EpochNumber, }, ) if err != nil { - panic(err) + return nil, err } } - return validatorSetUpdate + return validatorSetUpdate, nil } diff --git a/x/epoching/client/cli/tx.go b/x/epoching/client/cli/tx.go index 52ad580f9..42923beaf 100644 --- a/x/epoching/client/cli/tx.go +++ b/x/epoching/client/cli/tx.go @@ -2,8 +2,8 @@ package cli import ( "fmt" + "strconv" "strings" - "time" "github.com/spf13/cobra" @@ -17,10 +17,6 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -var ( - DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) -) - // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { cmd := &cobra.Command{ @@ -35,6 +31,7 @@ func GetTxCmd() *cobra.Command { NewDelegateCmd(), NewRedelegateCmd(), NewUnbondCmd(), + NewCancelUnbondingCmd(), ) return cmd @@ -73,7 +70,7 @@ $ %s tx epoching delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000%s --fro return err } - stakingMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, amount) + stakingMsg := stakingtypes.NewMsgDelegate(delAddr.String(), valAddr.String(), amount) msg := types.NewMsgWrappedDelegate(stakingMsg) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) @@ -123,7 +120,7 @@ $ %s tx epoching redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp return err } - stakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) + stakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr.String(), valSrcAddr.String(), valDstAddr.String(), amount) msg := types.NewMsgWrappedBeginRedelegate(stakingMsg) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) @@ -135,6 +132,56 @@ $ %s tx epoching redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp return cmd } +func NewCancelUnbondingCmd() *cobra.Command { + bech32PrefixValAddr := params.Bech32PrefixAccAddr + denom := params.DefaultBondDenom + + cmd := &cobra.Command{ + Use: "cancel-unbond [validator-addr] [amount] [creation-height]", + Short: "Cancel unbonding delegation and delegate back to the validator", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Cancel Unbonding Delegation and delegate back to the validator. + +Example: +$ %s tx staking cancel-unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100%s 2 --from mykey +`, + version.AppName, bech32PrefixValAddr, denom, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + creationHeight, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return err + } + + stakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr.String(), valAddr.String(), creationHeight, amount) + msg := types.NewMsgWrappedCancelUnbondingDelegation(stakingMsg) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + func NewUnbondCmd() *cobra.Command { bech32PrefixValAddr := params.Bech32PrefixAccAddr denom := params.DefaultBondDenom @@ -168,7 +215,7 @@ $ %s tx epoching unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100%s --from m return err } - stakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, amount) + stakingMsg := stakingtypes.NewMsgUndelegate(delAddr.String(), valAddr.String(), amount) msg := types.NewMsgWrappedUndelegate(stakingMsg) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) diff --git a/x/epoching/genesis.go b/x/epoching/genesis.go index 9cd920c89..efd7b985d 100644 --- a/x/epoching/genesis.go +++ b/x/epoching/genesis.go @@ -1,14 +1,14 @@ package epoching import ( + "context" "github.com/babylonchain/babylon/x/epoching/keeper" "github.com/babylonchain/babylon/x/epoching/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { // set params for this module if err := k.SetParams(ctx, genState.Params); err != nil { panic(err) @@ -25,7 +25,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) } // ExportGenesis returns the capability module's exported genesis. -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() genesis.Params = k.GetParams(ctx) diff --git a/x/epoching/genesis_test.go b/x/epoching/genesis_test.go index 85a21d31f..ae92a5024 100644 --- a/x/epoching/genesis_test.go +++ b/x/epoching/genesis_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/babylonchain/babylon/x/epoching" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/stretchr/testify/require" simapp "github.com/babylonchain/babylon/app" @@ -13,7 +12,7 @@ import ( func TestExportGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) if err := app.EpochingKeeper.SetParams(ctx, types.DefaultParams()); err != nil { panic(err) @@ -25,7 +24,7 @@ func TestExportGenesis(t *testing.T) { func TestInitGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) genesisState := types.GenesisState{ Params: types.Params{ diff --git a/x/epoching/keeper/apphash_chain.go b/x/epoching/keeper/apphash_chain.go index 511ebe336..3a36b85f4 100644 --- a/x/epoching/keeper/apphash_chain.go +++ b/x/epoching/keeper/apphash_chain.go @@ -1,51 +1,64 @@ package keeper import ( + "context" "crypto/sha256" "fmt" errorsmod "cosmossdk.io/errors" - "github.com/babylonchain/babylon/x/epoching/types" + "cosmossdk.io/store/prefix" "github.com/cometbft/cometbft/crypto/merkle" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/store/prefix" + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonchain/babylon/x/epoching/types" ) -func (k Keeper) setAppHash(ctx sdk.Context, height uint64, appHash []byte) { +func (k Keeper) setAppHash(ctx context.Context, height uint64, appHash []byte) { store := k.appHashStore(ctx) heightBytes := sdk.Uint64ToBigEndian(height) store.Set(heightBytes, appHash) } // GetAppHash gets the AppHash of the header at the given height -func (k Keeper) GetAppHash(ctx sdk.Context, height uint64) ([]byte, error) { +func (k Keeper) GetAppHash(ctx context.Context, height uint64) ([]byte, error) { store := k.appHashStore(ctx) heightBytes := sdk.Uint64ToBigEndian(height) appHash := store.Get(heightBytes) if appHash == nil { - return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "height %d is now known in DB yet", height) + return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "height %d is not known in DB yet", height) } return appHash, nil } // RecordAppHash stores the AppHash of the current header to KVStore -func (k Keeper) RecordAppHash(ctx sdk.Context) { - header := ctx.BlockHeader() - height := uint64(header.Height) - k.setAppHash(ctx, height, header.AppHash) +func (k Keeper) RecordAppHash(ctx context.Context) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + height := uint64(sdkCtx.HeaderInfo().Height) + appHash := sdkCtx.HeaderInfo().AppHash + // HACK: the app hash for the first height is set to nil + // instead of the hash of an empty byte slice as intended + // see proposed fix: https://github.com/cosmos/cosmos-sdk/pull/18524 + if height == 1 { + // $ echo -n '' | sha256sum + // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + emptyHash := sha256.Sum256([]byte{}) + appHash = emptyHash[:] + } + k.setAppHash(ctx, height, appHash) } -// GetAllAppHashsForEpoch fetches all AppHashs in the given epoch -func (k Keeper) GetAllAppHashsForEpoch(ctx sdk.Context, epoch *types.Epoch) ([][]byte, error) { +// GetAllAppHashesForEpoch fetches all AppHashes in the given epoch +func (k Keeper) GetAllAppHashesForEpoch(ctx context.Context, epoch *types.Epoch) ([][]byte, error) { // if this epoch is the most recent AND has not ended, then we cannot get all AppHashs for this epoch - if k.GetEpoch(ctx).EpochNumber == epoch.EpochNumber && !epoch.IsLastBlock(ctx) { - return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "GetAllAppHashsForEpoch can only be invoked when this epoch has ended") + if k.GetEpoch(ctx).EpochNumber == epoch.EpochNumber && !epoch.IsLastBlock(sdk.UnwrapSDKContext(ctx)) { + return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "GetAllAppHashesForEpoch can only be invoked when this epoch has ended") } // fetch each AppHash in this epoch appHashs := [][]byte{} - for i := epoch.FirstBlockHeight; i <= uint64(epoch.LastBlockHeader.Height); i++ { + for i := epoch.FirstBlockHeight; i <= epoch.GetLastBlockHeight(); i++ { appHash, err := k.GetAppHash(ctx, i) if err != nil { return nil, err @@ -57,21 +70,21 @@ func (k Keeper) GetAllAppHashsForEpoch(ctx sdk.Context, epoch *types.Epoch) ([][ } // ProveAppHashInEpoch generates a proof that the given appHash is in a given epoch -func (k Keeper) ProveAppHashInEpoch(ctx sdk.Context, height uint64, epochNumber uint64) (*tmcrypto.Proof, error) { +func (k Keeper) ProveAppHashInEpoch(ctx context.Context, height uint64, epochNumber uint64) (*cmtcrypto.Proof, error) { // ensure height is inside this epoch epoch, err := k.GetHistoricalEpoch(ctx, epochNumber) if err != nil { return nil, err } if !epoch.WithinBoundary(height) { - return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "the given height %d is not in epoch %d (interval [%d, %d])", height, epoch.EpochNumber, epoch.FirstBlockHeight, uint64(epoch.LastBlockHeader.Height)) + return nil, errorsmod.Wrapf(types.ErrInvalidHeight, "the given height %d is not in epoch %d (interval [%d, %d])", height, epoch.EpochNumber, epoch.FirstBlockHeight, epoch.GetLastBlockHeight()) } // calculate index of this height in this epoch idx := height - epoch.FirstBlockHeight // fetch all AppHashs, calculate Merkle tree and proof - appHashs, err := k.GetAllAppHashsForEpoch(ctx, epoch) + appHashs, err := k.GetAllAppHashesForEpoch(ctx, epoch) if err != nil { return nil, err } @@ -81,7 +94,7 @@ func (k Keeper) ProveAppHashInEpoch(ctx sdk.Context, height uint64, epochNumber } // VerifyAppHashInclusion verifies whether the given appHash is in the Merkle tree w.r.t. the appHashRoot -func VerifyAppHashInclusion(appHash []byte, appHashRoot []byte, proof *tmcrypto.Proof) error { +func VerifyAppHashInclusion(appHash []byte, appHashRoot []byte, proof *cmtcrypto.Proof) error { if len(appHash) != sha256.Size { return fmt.Errorf("appHash with length %d is not a Sha256 hash", len(appHash)) } @@ -103,7 +116,7 @@ func VerifyAppHashInclusion(appHash []byte, appHashRoot []byte, proof *tmcrypto. // prefix: AppHashKey // key: height // value: AppHash in bytes -func (k Keeper) appHashStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.AppHashKey) +func (k Keeper) appHashStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.AppHashKey) } diff --git a/x/epoching/keeper/apphash_chain_test.go b/x/epoching/keeper/apphash_chain_test.go index 15cae8998..3b4148dd9 100644 --- a/x/epoching/keeper/apphash_chain_test.go +++ b/x/epoching/keeper/apphash_chain_test.go @@ -4,11 +4,11 @@ import ( "math/rand" "testing" + "github.com/stretchr/testify/require" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" "github.com/babylonchain/babylon/x/epoching/keeper" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/stretchr/testify/require" ) func FuzzAppHashChain(f *testing.F) { @@ -16,41 +16,28 @@ func FuzzAppHashChain(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) + var err error - helper := testepoching.NewHelper(t) - ctx, k := helper.Ctx, helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx, k := helper.Ctx, helper.App.EpochingKeeper // ensure that the epoch info is correct at the genesis epoch := k.GetEpoch(ctx) - require.Equal(t, epoch.EpochNumber, uint64(0)) - require.Equal(t, epoch.FirstBlockHeight, uint64(0)) + require.Equal(t, epoch.EpochNumber, uint64(1)) + require.Equal(t, epoch.FirstBlockHeight, uint64(1)) // set a random epoch interval - epochInterval := r.Uint64()%100 + 2 // the epoch interval should at at least 2 - - params := types.Params{ - EpochInterval: epochInterval, - } - - if err := k.SetParams(ctx, params); err != nil { - panic(err) - } + epochInterval := k.GetParams(ctx).EpochInterval // reach the end of the 1st epoch - expectedHeight := epochInterval - expectedAppHashs := [][]byte{} + expectedHeight := epochInterval - 2 for i := uint64(0); i < expectedHeight; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - expectedAppHashs = append(expectedAppHashs, ctx.BlockHeader().AppHash) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } // ensure epoch number is 1 epoch = k.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) - // ensure appHashs are same as expectedAppHashs - appHashs, err := k.GetAllAppHashsForEpoch(ctx, epoch) - require.NoError(t, err) - require.Equal(t, expectedAppHashs, appHashs) - // ensure prover and verifier are correct randomHeightInEpoch := uint64(r.Intn(int(expectedHeight)) + 1) randomAppHash, err := k.GetAppHash(ctx, randomHeightInEpoch) diff --git a/x/epoching/keeper/drop_validator_msg_decorator.go b/x/epoching/keeper/drop_validator_msg_decorator.go index 6e0f1509a..c9f3baa45 100644 --- a/x/epoching/keeper/drop_validator_msg_decorator.go +++ b/x/epoching/keeper/drop_validator_msg_decorator.go @@ -24,7 +24,7 @@ func NewDropValidatorMsgDecorator(ek Keeper) *DropValidatorMsgDecorator { // - MsgDelegate // - MsgUndelegate // - MsgBeginRedelegate -// TODO (non-urgent): after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation +// - MsgCancelUnbondingDelegation func (qmd DropValidatorMsgDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { // skip if at genesis block, as genesis state contains txs that bootstrap the initial validator set if ctx.BlockHeight() == 0 { @@ -43,7 +43,7 @@ func (qmd DropValidatorMsgDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu // IsValidatorRelatedMsg checks if the given message is of non-wrapped type, which should be rejected func (qmd DropValidatorMsgDecorator) IsValidatorRelatedMsg(msg sdk.Msg) bool { switch msg.(type) { - case *stakingtypes.MsgCreateValidator, *stakingtypes.MsgDelegate, *stakingtypes.MsgUndelegate, *stakingtypes.MsgBeginRedelegate: + case *stakingtypes.MsgCreateValidator, *stakingtypes.MsgDelegate, *stakingtypes.MsgUndelegate, *stakingtypes.MsgBeginRedelegate, *stakingtypes.MsgCancelUnbondingDelegation: return true default: return false diff --git a/x/epoching/keeper/drop_validator_msg_decorator_test.go b/x/epoching/keeper/drop_validator_msg_decorator_test.go index e6c5ffa17..a6411e373 100644 --- a/x/epoching/keeper/drop_validator_msg_decorator_test.go +++ b/x/epoching/keeper/drop_validator_msg_decorator_test.go @@ -19,6 +19,7 @@ func TestDropValidatorMsgDecorator(t *testing.T) { {&stakingtypes.MsgDelegate{}, true}, {&stakingtypes.MsgUndelegate{}, true}, {&stakingtypes.MsgBeginRedelegate{}, true}, + {&stakingtypes.MsgCancelUnbondingDelegation{}, true}, // allowed message types {&stakingtypes.MsgEditValidator{}, false}, } diff --git a/x/epoching/keeper/epoch_msg_queue.go b/x/epoching/keeper/epoch_msg_queue.go index e2dbd4426..52970fc52 100644 --- a/x/epoching/keeper/epoch_msg_queue.go +++ b/x/epoching/keeper/epoch_msg_queue.go @@ -1,16 +1,20 @@ package keeper import ( + "context" "fmt" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/runtime" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) // InitMsgQueue initialises the msg queue length of the current epoch to 0 -func (k Keeper) InitMsgQueue(ctx sdk.Context) { +func (k Keeper) InitMsgQueue(ctx context.Context) { store := k.msgQueueLengthStore(ctx) epochNumber := k.GetEpoch(ctx).EpochNumber @@ -20,7 +24,7 @@ func (k Keeper) InitMsgQueue(ctx sdk.Context) { } // GetQueueLength fetches the number of queued messages of a given epoch -func (k Keeper) GetQueueLength(ctx sdk.Context, epochNumber uint64) uint64 { +func (k Keeper) GetQueueLength(ctx context.Context, epochNumber uint64) uint64 { store := k.msgQueueLengthStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) @@ -33,14 +37,14 @@ func (k Keeper) GetQueueLength(ctx sdk.Context, epochNumber uint64) uint64 { return sdk.BigEndianToUint64(bz) } -// GetQueueLength fetches the number of queued messages of the current epoch -func (k Keeper) GetCurrentQueueLength(ctx sdk.Context) uint64 { +// GetCurrentQueueLength fetches the number of queued messages of the current epoch +func (k Keeper) GetCurrentQueueLength(ctx context.Context) uint64 { epochNumber := k.GetEpoch(ctx).EpochNumber return k.GetQueueLength(ctx, epochNumber) } // incCurrentQueueLength adds the queue length of the current epoch by 1 -func (k Keeper) incCurrentQueueLength(ctx sdk.Context) { +func (k Keeper) incCurrentQueueLength(ctx context.Context) { store := k.msgQueueLengthStore(ctx) epochNumber := k.GetEpoch(ctx).EpochNumber @@ -54,7 +58,7 @@ func (k Keeper) incCurrentQueueLength(ctx sdk.Context) { } // EnqueueMsg enqueues a message to the queue of the current epoch -func (k Keeper) EnqueueMsg(ctx sdk.Context, msg types.QueuedMessage) { +func (k Keeper) EnqueueMsg(ctx context.Context, msg types.QueuedMessage) { epochNumber := k.GetEpoch(ctx).EpochNumber store := k.msgQueueStore(ctx, epochNumber) @@ -73,12 +77,12 @@ func (k Keeper) EnqueueMsg(ctx sdk.Context, msg types.QueuedMessage) { } // GetEpochMsgs returns the set of messages queued in a given epoch -func (k Keeper) GetEpochMsgs(ctx sdk.Context, epochNumber uint64) []*types.QueuedMessage { +func (k Keeper) GetEpochMsgs(ctx context.Context, epochNumber uint64) []*types.QueuedMessage { queuedMsgs := []*types.QueuedMessage{} store := k.msgQueueStore(ctx, epochNumber) // add each queued msg to queuedMsgs - iterator := sdk.KVStorePrefixIterator(store, nil) + iterator := storetypes.KVStorePrefixIterator(store, nil) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { queuedMsgBytes := iterator.Value() @@ -97,13 +101,13 @@ func (k Keeper) GetEpochMsgs(ctx sdk.Context, epochNumber uint64) []*types.Queue } // GetCurrentEpochMsgs returns the set of messages queued in the current epoch -func (k Keeper) GetCurrentEpochMsgs(ctx sdk.Context) []*types.QueuedMessage { +func (k Keeper) GetCurrentEpochMsgs(ctx context.Context) []*types.QueuedMessage { epochNumber := k.GetEpoch(ctx).EpochNumber return k.GetEpochMsgs(ctx, epochNumber) } // HandleQueuedMsg unwraps a QueuedMessage and forwards it to the staking module -func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk.Result, error) { +func (k Keeper) HandleQueuedMsg(ctx context.Context, msg *types.QueuedMessage) (*sdk.Result, error) { var ( unwrappedMsgWithType sdk.Msg err error @@ -120,7 +124,7 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk // Create a new Context based off of the existing Context with a MultiStore branch // in case message processing fails. At this point, the MultiStore is a branch of a branch. - handlerCtx, msCache := cacheTxContext(ctx, msg.TxId, msg.MsgId, msg.BlockHeight) + handlerCtx, msCache := cacheTxContext(sdk.UnwrapSDKContext(ctx), msg.TxId, msg.MsgId, msg.BlockHeight) // handle the unwrapped message result, err := handler(handlerCtx, unwrappedMsgWithType) @@ -135,7 +139,8 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk switch unwrappedMsg := msg.Msg.(type) { case *types.QueuedMessage_MsgCreateValidator: // handle self-delegation - delAddr, err := sdk.AccAddressFromBech32(unwrappedMsg.MsgCreateValidator.DelegatorAddress) + // Delegator and Validator address are the same + delAddr, err := sdk.AccAddressFromBech32(unwrappedMsg.MsgCreateValidator.ValidatorAddress) if err != nil { return nil, err } @@ -143,11 +148,12 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk if err != nil { return nil, err } + amount := &unwrappedMsg.MsgCreateValidator.Value // self-bonded to the created validator - if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_CREATED); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_CREATED); err != nil { return nil, err } - if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_BONDED); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_BONDED); err != nil { return nil, err } case *types.QueuedMessage_MsgDelegate: @@ -159,11 +165,12 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk if err != nil { return nil, err } + amount := &unwrappedMsg.MsgDelegate.Amount // created and bonded to the validator - if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_CREATED); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_CREATED); err != nil { return nil, err } - if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_BONDED); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_BONDED); err != nil { return nil, err } case *types.QueuedMessage_MsgUndelegate: @@ -175,9 +182,10 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk if err != nil { return nil, err } + amount := &unwrappedMsg.MsgUndelegate.Amount // unbonding from the validator // (in `ApplyMatureUnbonding`) AFTER mature, unbonded from the validator - if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_UNBONDING); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_UNBONDING); err != nil { return nil, err } case *types.QueuedMessage_MsgBeginRedelegate: @@ -189,9 +197,24 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk if err != nil { return nil, err } + amount := &unwrappedMsg.MsgBeginRedelegate.Amount // unbonding from the source validator // (in `ApplyMatureUnbonding`) AFTER mature, unbonded from the source validator, created/bonded to the destination validator - if err := k.RecordNewDelegationState(ctx, delAddr, srcValAddr, types.BondState_UNBONDING); err != nil { + if err := k.RecordNewDelegationState(ctx, delAddr, srcValAddr, amount, types.BondState_UNBONDING); err != nil { + return nil, err + } + case *types.QueuedMessage_MsgCancelUnbondingDelegation: + delAddr, err := sdk.AccAddressFromBech32(unwrappedMsg.MsgCancelUnbondingDelegation.DelegatorAddress) + if err != nil { + return nil, err + } + valAddr, err := sdk.ValAddressFromBech32(unwrappedMsg.MsgCancelUnbondingDelegation.ValidatorAddress) + if err != nil { + return nil, err + } + amount := &unwrappedMsg.MsgCancelUnbondingDelegation.Amount + // this delegation is now bonded again + if err := k.RecordNewDelegationState(ctx, delAddr, valAddr, amount, types.BondState_BONDED); err != nil { return nil, err } default: @@ -202,20 +225,18 @@ func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk } // based on a function with the same name in `baseapp.go` -func cacheTxContext(ctx sdk.Context, txid []byte, msgid []byte, height uint64) (sdk.Context, sdk.CacheMultiStore) { +func cacheTxContext(ctx sdk.Context, txid []byte, msgid []byte, height uint64) (sdk.Context, storetypes.CacheMultiStore) { ms := ctx.MultiStore() // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 msCache := ms.CacheMultiStore() if msCache.TracingEnabled() { msCache = msCache.SetTracingContext( - sdk.TraceContext( - map[string]interface{}{ - "txHash": fmt.Sprintf("%X", txid), - "msgHash": fmt.Sprintf("%X", msgid), - "height": fmt.Sprintf("%d", height), - }, - ), - ).(sdk.CacheMultiStore) + map[string]interface{}{ + "txHash": fmt.Sprintf("%X", txid), + "msgHash": fmt.Sprintf("%X", msgid), + "height": fmt.Sprintf("%d", height), + }, + ).(storetypes.CacheMultiStore) } return ctx.WithMultiStore(msCache), msCache @@ -225,9 +246,9 @@ func cacheTxContext(ctx sdk.Context, txid []byte, msgid []byte, height uint64) ( // prefix: MsgQueueKey || epochNumber // key: index // value: msg -func (k Keeper) msgQueueStore(ctx sdk.Context, epochNumber uint64) prefix.Store { - store := ctx.KVStore(k.storeKey) - msgQueueStore := prefix.NewStore(store, types.MsgQueueKey) +func (k Keeper) msgQueueStore(ctx context.Context, epochNumber uint64) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + msgQueueStore := prefix.NewStore(storeAdapter, types.MsgQueueKey) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) return prefix.NewStore(msgQueueStore, epochNumberBytes) } @@ -236,7 +257,7 @@ func (k Keeper) msgQueueStore(ctx sdk.Context, epochNumber uint64) prefix.Store // prefix: QueueLengthKey // key: epochNumber // value: queue length -func (k Keeper) msgQueueLengthStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.QueueLengthKey) +func (k Keeper) msgQueueLengthStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.QueueLengthKey) } diff --git a/x/epoching/keeper/epoch_msg_queue_test.go b/x/epoching/keeper/epoch_msg_queue_test.go index 8d41bd685..22703df73 100644 --- a/x/epoching/keeper/epoch_msg_queue_test.go +++ b/x/epoching/keeper/epoch_msg_queue_test.go @@ -1,17 +1,19 @@ package keeper_test import ( - "github.com/babylonchain/babylon/testutil/datagen" "math/rand" "testing" - appparams "github.com/babylonchain/babylon/app/params" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + "github.com/babylonchain/babylon/x/epoching/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + appparams "github.com/babylonchain/babylon/app/params" ) var ( @@ -24,15 +26,13 @@ func FuzzEnqueueMsg(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelper(t) - ctx, keeper := helper.Ctx, helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx, keeper := helper.Ctx, helper.App.EpochingKeeper // ensure that the epoch msg queue is correct at the genesis require.Empty(t, keeper.GetCurrentEpochMsgs(ctx)) require.Equal(t, uint64(0), keeper.GetCurrentQueueLength(ctx)) - // enter the 1st block and thus epoch 1 - // Note that the genesis block does not trigger BeginBlock or EndBlock - ctx = helper.GenAndApplyEmptyBlock(r) + // at epoch 1 right now epoch := keeper.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) // ensure that the epoch msg queue is correct at epoch 1 @@ -67,8 +67,11 @@ func FuzzHandleQueuedMsg_MsgWrappedDelegate(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelperWithValSet(t) - ctx, keeper, genAccs := helper.Ctx, helper.EpochingKeeper, helper.GenAccs + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ctx, keeper, genAccs := helper.Ctx, helper.App.EpochingKeeper, helper.GenAccs params := keeper.GetParams(ctx) // get genesis account's address, whose holder will be the delegator @@ -76,8 +79,7 @@ func FuzzHandleQueuedMsg_MsgWrappedDelegate(f *testing.F) { require.NotEmpty(t, genAccs) genAddr := genAccs[0].GetAddress() - // BeginBlock of block 1, and thus entering epoch 1 - ctx = helper.BeginBlock(r) + // at epoch 1 right now epoch := keeper.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) @@ -103,12 +105,10 @@ func FuzzHandleQueuedMsg_MsgWrappedDelegate(f *testing.F) { epochMsgs := keeper.GetCurrentEpochMsgs(ctx) require.Equal(t, numNewDels, int64(len(epochMsgs))) - // EndBlock of block 1 - ctx = helper.EndBlock() - // go to BeginBlock of block 11, and thus entering epoch 2 for i := uint64(0); i < params.EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = keeper.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) @@ -119,7 +119,7 @@ func FuzzHandleQueuedMsg_MsgWrappedDelegate(f *testing.F) { // ensure the voting power has been added w.r.t. the newly delegated tokens valPower2, err := keeper.GetCurrentValidatorVotingPower(ctx, val) require.NoError(t, err) - addedPower := helper.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewDels)) + addedPower := helper.App.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewDels)) require.Equal(t, valPower+addedPower, valPower2) }) } @@ -131,22 +131,24 @@ func FuzzHandleQueuedMsg_MsgWrappedUndelegate(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelperWithValSet(t) - _, keeper, genAccs := helper.Ctx, helper.EpochingKeeper, helper.GenAccs + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ctx, keeper, genAccs := helper.Ctx, helper.App.EpochingKeeper, helper.GenAccs // get genesis account's address, whose holder will be the delegator require.NotNil(t, genAccs) require.NotEmpty(t, genAccs) genAddr := genAccs[0].GetAddress() - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // at epoch 1 right now epoch := keeper.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) - valSet1 := helper.EpochingKeeper.GetCurrentValidatorSet(helper.Ctx) + valSet1 := helper.App.EpochingKeeper.GetCurrentValidatorSet(helper.Ctx) val := valSet1[0].Addr // validator to be undelegated - valPower, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val) + valPower, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val) require.NoError(t, err) // ensure the validator's lifecycle data is generated @@ -168,12 +170,10 @@ func FuzzHandleQueuedMsg_MsgWrappedUndelegate(f *testing.F) { epochMsgs := keeper.GetCurrentEpochMsgs(ctx) require.Equal(t, numNewUndels, int64(len(epochMsgs))) - // EndBlock of block 1 - ctx = helper.EndBlock() - // enter epoch 2 for i := uint64(0); i < keeper.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = keeper.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) @@ -182,13 +182,14 @@ func FuzzHandleQueuedMsg_MsgWrappedUndelegate(f *testing.F) { require.Empty(t, keeper.GetCurrentEpochMsgs(ctx)) // ensure the voting power has been reduced w.r.t. the unbonding tokens - valPower2, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val) + valPower2, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val) require.NoError(t, err) - reducedPower := helper.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewUndels)) + reducedPower := helper.App.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewUndels)) require.Equal(t, valPower-reducedPower, valPower2) // ensure the genesis account has these unbonding tokens - unbondingDels := helper.StakingKeeper.GetAllUnbondingDelegations(ctx, genAddr) + unbondingDels, err := helper.App.StakingKeeper.GetAllUnbondingDelegations(ctx, genAddr) + require.NoError(t, err) require.Equal(t, 1, len(unbondingDels)) // there is only 1 type of tokens // from cosmos v47, all undelegations made at the same height are represented @@ -205,27 +206,29 @@ func FuzzHandleQueuedMsg_MsgWrappedBeginRedelegate(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelperWithValSet(t) - _, keeper, genAccs := helper.Ctx, helper.EpochingKeeper, helper.GenAccs + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ctx, keeper, genAccs := helper.Ctx, helper.App.EpochingKeeper, helper.GenAccs // get genesis account's address, whose holder will be the delegator require.NotNil(t, genAccs) require.NotEmpty(t, genAccs) genAddr := genAccs[0].GetAddress() - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) + // at epoch 1 right now epoch := keeper.GetEpoch(ctx) require.Equal(t, uint64(1), epoch.EpochNumber) - valSet1 := helper.EpochingKeeper.GetCurrentValidatorSet(ctx) + valSet1 := helper.App.EpochingKeeper.GetCurrentValidatorSet(ctx) // 2 validators, where the operator will redelegate some token from val1 to val2 val1 := valSet1[0].Addr - val1Power, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val1) + val1Power, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val1) require.NoError(t, err) val2 := valSet1[1].Addr - val2Power, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val2) + val2Power, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val2) require.NoError(t, err) require.Equal(t, val1Power, val2Power) @@ -249,12 +252,10 @@ func FuzzHandleQueuedMsg_MsgWrappedBeginRedelegate(f *testing.F) { epochMsgs := keeper.GetCurrentEpochMsgs(ctx) require.Equal(t, numNewRedels, int64(len(epochMsgs))) - // EndBlock of block 1 - ctx = helper.EndBlock() - // enter epoch 2 for i := uint64(0); i < keeper.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } epoch = keeper.GetEpoch(ctx) require.Equal(t, uint64(2), epoch.EpochNumber) @@ -266,18 +267,19 @@ func FuzzHandleQueuedMsg_MsgWrappedBeginRedelegate(f *testing.F) { // Note that in Cosmos SDK, redelegation happens upon the next `EndBlock`, rather than waiting for 14 days. // This is because redelegation does not affect PoS security: upon redelegation requests, no token is leaving the system. // SImilarly, in Babylon, redelegation happens unconditionally upon `EndEpoch`, rather than depending on checkpoint status. - val1Power2, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val1) + val1Power2, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val1) require.NoError(t, err) - val2Power2, err := helper.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val2) + val2Power2, err := helper.App.EpochingKeeper.GetCurrentValidatorVotingPower(ctx, val2) require.NoError(t, err) - redelegatedPower := helper.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewRedels)) + redelegatedPower := helper.App.StakingKeeper.TokensToConsensusPower(ctx, coinWithOnePower.Amount.MulRaw(numNewRedels)) // ensure the voting power of val1 has reduced require.Equal(t, val1Power-redelegatedPower, val1Power2) // ensure the voting power of val2 has increased require.Equal(t, val2Power+redelegatedPower, val2Power2) // ensure the genesis account has these redelegating tokens - redelegations := helper.StakingKeeper.GetAllRedelegations(ctx, genAddr, val1, val2) + redelegations, err := helper.App.StakingKeeper.GetAllRedelegations(ctx, genAddr, val1, val2) + require.NoError(t, err) require.Equal(t, 1, len(redelegations)) // there is only 1 type of tokens require.Equal(t, numNewRedels, int64(len(redelegations[0].Entries))) // there are numNewRedels entries for _, entry := range redelegations[0].Entries { diff --git a/x/epoching/keeper/epoch_slashed_val_set.go b/x/epoching/keeper/epoch_slashed_val_set.go index 71feae801..a52b89624 100644 --- a/x/epoching/keeper/epoch_slashed_val_set.go +++ b/x/epoching/keeper/epoch_slashed_val_set.go @@ -1,21 +1,23 @@ package keeper import ( + "context" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) // setSlashedVotingPower sets the total amount of voting power that has been slashed in the epoch -func (k Keeper) setSlashedVotingPower(ctx sdk.Context, epochNumber uint64, power int64) { +func (k Keeper) setSlashedVotingPower(ctx context.Context, epochNumber uint64, power int64) { store := k.slashedVotingPowerStore(ctx) // key: epochNumber epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) // value: power - powerBytes, err := sdk.NewInt(power).Marshal() + powerBytes, err := sdkmath.NewInt(power).Marshal() if err != nil { panic(errorsmod.Wrap(types.ErrMarshal, err.Error())) } @@ -25,13 +27,13 @@ func (k Keeper) setSlashedVotingPower(ctx sdk.Context, epochNumber uint64, power // InitSlashedVotingPower sets the slashed voting power of the current epoch to 0 // This is called upon initialising the genesis state and upon a new epoch -func (k Keeper) InitSlashedVotingPower(ctx sdk.Context) { +func (k Keeper) InitSlashedVotingPower(ctx context.Context) { epochNumber := k.GetEpoch(ctx).EpochNumber k.setSlashedVotingPower(ctx, epochNumber, 0) } // GetSlashedVotingPower fetches the amount of slashed voting power of a given epoch -func (k Keeper) GetSlashedVotingPower(ctx sdk.Context, epochNumber uint64) int64 { +func (k Keeper) GetSlashedVotingPower(ctx context.Context, epochNumber uint64) int64 { store := k.slashedVotingPowerStore(ctx) // key: epochNumber @@ -41,7 +43,7 @@ func (k Keeper) GetSlashedVotingPower(ctx sdk.Context, epochNumber uint64) int64 panic(types.ErrUnknownSlashedVotingPower) } // get value - var slashedVotingPower math.Int + var slashedVotingPower sdkmath.Int if err := slashedVotingPower.Unmarshal(bz); err != nil { panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) } @@ -51,14 +53,14 @@ func (k Keeper) GetSlashedVotingPower(ctx sdk.Context, epochNumber uint64) int64 // AddSlashedValidator adds a slashed validator to the set of the current epoch // This is called upon hook `BeforeValidatorSlashed` exposed by the staking module -func (k Keeper) AddSlashedValidator(ctx sdk.Context, valAddr sdk.ValAddress) error { +func (k Keeper) AddSlashedValidator(ctx context.Context, valAddr sdk.ValAddress) error { epochNumber := k.GetEpoch(ctx).EpochNumber store := k.slashedValSetStore(ctx, epochNumber) thisVotingPower, err := k.GetValidatorVotingPower(ctx, epochNumber, valAddr) if err != nil { panic(errorsmod.Wrap(types.ErrMarshal, err.Error())) } - thisVotingPowerBytes, err := sdk.NewInt(thisVotingPower).Marshal() + thisVotingPowerBytes, err := sdkmath.NewInt(thisVotingPower).Marshal() if err != nil { panic(errorsmod.Wrap(types.ErrMarshal, err.Error())) } @@ -79,7 +81,7 @@ func (k Keeper) AddSlashedValidator(ctx sdk.Context, valAddr sdk.ValAddress) err } // GetSlashedValidators returns the set of slashed validators of a given epoch -func (k Keeper) GetSlashedValidators(ctx sdk.Context, epochNumber uint64) types.ValidatorSet { +func (k Keeper) GetSlashedValidators(ctx context.Context, epochNumber uint64) types.ValidatorSet { valSet := types.ValidatorSet{} store := k.slashedValSetStore(ctx, epochNumber) // add each valAddr, which is the key @@ -91,7 +93,7 @@ func (k Keeper) GetSlashedValidators(ctx sdk.Context, epochNumber uint64) types. if powerBytes == nil { panic(types.ErrUnknownValidator) } - var power math.Int + var power sdkmath.Int if err := power.Unmarshal(powerBytes); err != nil { panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) } @@ -104,7 +106,7 @@ func (k Keeper) GetSlashedValidators(ctx sdk.Context, epochNumber uint64) types. // ClearSlashedValidators removes all slashed validators in the set // TODO: This is called upon the epoch is checkpointed -func (k Keeper) ClearSlashedValidators(ctx sdk.Context, epochNumber uint64) { +func (k Keeper) ClearSlashedValidators(ctx context.Context, epochNumber uint64) { // prefix : SlashedValidatorSetKey || epochNumber store := k.slashedValSetStore(ctx, epochNumber) @@ -123,16 +125,16 @@ func (k Keeper) ClearSlashedValidators(ctx sdk.Context, epochNumber uint64) { // slashedValSetStore returns the KVStore of the slashed validator set for a given epoch // prefix : SlashedValidatorSetKey || epochNumber -func (k Keeper) slashedValSetStore(ctx sdk.Context, epochNumber uint64) prefix.Store { - store := ctx.KVStore(k.storeKey) - slashedValStore := prefix.NewStore(store, types.SlashedValidatorSetKey) +func (k Keeper) slashedValSetStore(ctx context.Context, epochNumber uint64) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + slashedValStore := prefix.NewStore(storeAdapter, types.SlashedValidatorSetKey) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) return prefix.NewStore(slashedValStore, epochNumberBytes) } // slashedVotingPower returns the KVStore of the slashed voting power // prefix: SlashedVotingPowerKey -func (k Keeper) slashedVotingPowerStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.SlashedVotingPowerKey) +func (k Keeper) slashedVotingPowerStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.SlashedVotingPowerKey) } diff --git a/x/epoching/keeper/epoch_slashed_val_set_test.go b/x/epoching/keeper/epoch_slashed_val_set_test.go index 9c6b9cb33..90ae8ad4b 100644 --- a/x/epoching/keeper/epoch_slashed_val_set_test.go +++ b/x/epoching/keeper/epoch_slashed_val_set_test.go @@ -1,25 +1,37 @@ package keeper_test import ( - "github.com/babylonchain/babylon/testutil/datagen" "math/rand" "sort" "testing" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" + sdkmath "cosmossdk.io/math" + + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/x/epoching/types" ) func FuzzSlashedValSet(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) + var err error + + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ctx, keeper, stakingKeeper := helper.Ctx, helper.App.EpochingKeeper, helper.App.StakingKeeper + getValSet := keeper.GetValidatorSet(ctx, 1) - helper := testepoching.NewHelperWithValSet(t) - ctx, keeper, stakingKeeper := helper.Ctx, helper.EpochingKeeper, helper.StakingKeeper - getValSet := keeper.GetValidatorSet(ctx, 0) + // at epoch 1 right now + epoch := keeper.GetEpoch(ctx) + require.Equal(t, uint64(1), epoch.EpochNumber) // slash a random subset of validators numSlashed := r.Intn(len(getValSet)) @@ -27,7 +39,8 @@ func FuzzSlashedValSet(f *testing.F) { for i := 0; i < numSlashed; i++ { idx := r.Intn(len(getValSet)) slashedVal := getValSet[idx] - stakingKeeper.Slash(ctx, sdk.ConsAddress(slashedVal.Addr), 0, slashedVal.Power, sdk.OneDec()) + _, err = stakingKeeper.Slash(ctx, slashedVal.Addr, 0, slashedVal.Power, sdkmath.LegacyOneDec()) + require.NoError(t, err) // add the slashed validator to the slashed validator set excpectedSlashedVals = append(excpectedSlashedVals, slashedVal.Addr) // remove the slashed validator from the validator set in order to avoid slashing a validator more than once @@ -35,7 +48,7 @@ func FuzzSlashedValSet(f *testing.F) { } // check whether the slashed validator set in DB is consistent or not - actualSlashedVals := keeper.GetSlashedValidators(ctx, 0) + actualSlashedVals := keeper.GetSlashedValidators(ctx, 1) require.Equal(t, len(excpectedSlashedVals), len(actualSlashedVals)) sortVals(excpectedSlashedVals) actualSlashedVals = types.NewSortedValidatorSet(actualSlashedVals) @@ -43,12 +56,17 @@ func FuzzSlashedValSet(f *testing.F) { require.Equal(t, excpectedSlashedVals[i], actualSlashedVals[i].GetValAddress()) } - // go to the 1st block and thus epoch 1 - ctx = helper.GenAndApplyEmptyBlock(r) - epochNumber := keeper.GetEpoch(ctx).EpochNumber - require.Equal(t, uint64(1), epochNumber) + // go to epoch 2 + epochInterval := keeper.GetParams(ctx).EpochInterval + for i := uint64(0); i < epochInterval; i++ { + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) + } + epoch = keeper.GetEpoch(ctx) + require.Equal(t, uint64(2), epoch.EpochNumber) + // no validator is slashed in epoch 1 - require.Empty(t, keeper.GetSlashedValidators(ctx, 1)) + require.Empty(t, keeper.GetSlashedValidators(ctx, 2)) }) } diff --git a/x/epoching/keeper/epoch_val_set.go b/x/epoching/keeper/epoch_val_set.go index 8f7de9a85..4095df178 100644 --- a/x/epoching/keeper/epoch_val_set.go +++ b/x/epoching/keeper/epoch_val_set.go @@ -1,16 +1,17 @@ package keeper import ( + "context" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/epoching/types" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) // GetValidatorSet returns the set of validators of a given epoch, where the validators are ordered by their address in ascending order -func (k Keeper) GetValidatorSet(ctx sdk.Context, epochNumber uint64) types.ValidatorSet { +func (k Keeper) GetValidatorSet(ctx context.Context, epochNumber uint64) types.ValidatorSet { vals := []types.Validator{} store := k.valSetStore(ctx, epochNumber) @@ -19,7 +20,7 @@ func (k Keeper) GetValidatorSet(ctx sdk.Context, epochNumber uint64) types.Valid for ; iterator.Valid(); iterator.Next() { addr := sdk.ValAddress(iterator.Key()) powerBytes := iterator.Value() - var power math.Int + var power sdkmath.Int if err := power.Unmarshal(powerBytes); err != nil { panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) } @@ -32,34 +33,22 @@ func (k Keeper) GetValidatorSet(ctx sdk.Context, epochNumber uint64) types.Valid return types.NewSortedValidatorSet(vals) } -func (k Keeper) GetCurrentValidatorSet(ctx sdk.Context) types.ValidatorSet { +func (k Keeper) GetCurrentValidatorSet(ctx context.Context) types.ValidatorSet { epochNumber := k.GetEpoch(ctx).EpochNumber return k.GetValidatorSet(ctx, epochNumber) } -func (k Keeper) GetValidatorPubkey(ctx sdk.Context, valAddr sdk.ValAddress) (cryptotypes.PubKey, bool) { - validator, found := k.stk.GetValidator(ctx, valAddr) - if !found { - return nil, false - } - pubkey, err := validator.ConsPubKey() - if err != nil { - return nil, false - } - return pubkey, true -} - // InitValidatorSet stores the validator set in the beginning of the current epoch // This is called upon BeginBlock -func (k Keeper) InitValidatorSet(ctx sdk.Context) { +func (k Keeper) InitValidatorSet(ctx context.Context) { epochNumber := k.GetEpoch(ctx).EpochNumber store := k.valSetStore(ctx, epochNumber) totalPower := int64(0) // store the validator set - k.stk.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { + err := k.stk.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { addrBytes := []byte(addr) - powerBytes, err := sdk.NewInt(power).Marshal() + powerBytes, err := sdkmath.NewInt(power).Marshal() if err != nil { panic(errorsmod.Wrap(types.ErrMarshal, err.Error())) } @@ -69,9 +58,13 @@ func (k Keeper) InitValidatorSet(ctx sdk.Context) { return false }) + if err != nil { + panic(err) + } + // store total voting power of this validator set epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) - totalPowerBytes, err := sdk.NewInt(totalPower).Marshal() + totalPowerBytes, err := sdkmath.NewInt(totalPower).Marshal() if err != nil { panic(errorsmod.Wrap(types.ErrMarshal, err.Error())) } @@ -80,7 +73,7 @@ func (k Keeper) InitValidatorSet(ctx sdk.Context) { // ClearValidatorSet removes the validator set of a given epoch // TODO: This is called upon the epoch is checkpointed -func (k Keeper) ClearValidatorSet(ctx sdk.Context, epochNumber uint64) { +func (k Keeper) ClearValidatorSet(ctx context.Context, epochNumber uint64) { store := k.valSetStore(ctx, epochNumber) iterator := store.Iterator(nil, nil) defer iterator.Close() @@ -96,14 +89,14 @@ func (k Keeper) ClearValidatorSet(ctx sdk.Context, epochNumber uint64) { } // GetValidatorVotingPower returns the voting power of a given validator in a given epoch -func (k Keeper) GetValidatorVotingPower(ctx sdk.Context, epochNumber uint64, valAddr sdk.ValAddress) (int64, error) { +func (k Keeper) GetValidatorVotingPower(ctx context.Context, epochNumber uint64, valAddr sdk.ValAddress) (int64, error) { store := k.valSetStore(ctx, epochNumber) powerBytes := store.Get(valAddr) if powerBytes == nil { return 0, types.ErrUnknownValidator } - var power math.Int + var power sdkmath.Int if err := power.Unmarshal(powerBytes); err != nil { panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) } @@ -111,20 +104,20 @@ func (k Keeper) GetValidatorVotingPower(ctx sdk.Context, epochNumber uint64, val return power.Int64(), nil } -func (k Keeper) GetCurrentValidatorVotingPower(ctx sdk.Context, valAddr sdk.ValAddress) (int64, error) { +func (k Keeper) GetCurrentValidatorVotingPower(ctx context.Context, valAddr sdk.ValAddress) (int64, error) { epochNumber := k.GetEpoch(ctx).EpochNumber return k.GetValidatorVotingPower(ctx, epochNumber, valAddr) } // GetTotalVotingPower returns the total voting power of a given epoch -func (k Keeper) GetTotalVotingPower(ctx sdk.Context, epochNumber uint64) int64 { +func (k Keeper) GetTotalVotingPower(ctx context.Context, epochNumber uint64) int64 { epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) store := k.votingPowerStore(ctx) powerBytes := store.Get(epochNumberBytes) if powerBytes == nil { panic(types.ErrUnknownTotalVotingPower) } - var power math.Int + var power sdkmath.Int if err := power.Unmarshal(powerBytes); err != nil { panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) } @@ -135,9 +128,9 @@ func (k Keeper) GetTotalVotingPower(ctx sdk.Context, epochNumber uint64) int64 { // prefix: ValidatorSetKey || epochNumber // key: string(address) // value: voting power (in int64 as per Cosmos SDK) -func (k Keeper) valSetStore(ctx sdk.Context, epochNumber uint64) prefix.Store { - store := ctx.KVStore(k.storeKey) - valSetStore := prefix.NewStore(store, types.ValidatorSetKey) +func (k Keeper) valSetStore(ctx context.Context, epochNumber uint64) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + valSetStore := prefix.NewStore(storeAdapter, types.ValidatorSetKey) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) return prefix.NewStore(valSetStore, epochNumberBytes) } @@ -146,7 +139,7 @@ func (k Keeper) valSetStore(ctx sdk.Context, epochNumber uint64) prefix.Store { // prefix: ValidatorSetKey // key: epochNumber // value: total voting power (in int64 as per Cosmos SDK) -func (k Keeper) votingPowerStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.VotingPowerKey) +func (k Keeper) votingPowerStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.VotingPowerKey) } diff --git a/x/epoching/keeper/epoch_val_set_test.go b/x/epoching/keeper/epoch_val_set_test.go index 842330805..8bc1e3b89 100644 --- a/x/epoching/keeper/epoch_val_set_test.go +++ b/x/epoching/keeper/epoch_val_set_test.go @@ -1,11 +1,12 @@ package keeper_test import ( - "github.com/babylonchain/babylon/testutil/datagen" "math/rand" "testing" - "github.com/babylonchain/babylon/x/epoching/testepoching" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) @@ -15,9 +16,13 @@ func FuzzEpochValSet(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelperWithValSet(t) - ctx, keeper := helper.Ctx, helper.EpochingKeeper - valSet := helper.StakingKeeper.GetLastValidators(helper.Ctx) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ctx, keeper := helper.Ctx, helper.App.EpochingKeeper + valSet, err := helper.App.StakingKeeper.GetLastValidators(helper.Ctx) + require.NoError(t, err) getValSet := keeper.GetValidatorSet(ctx, 0) require.Equal(t, len(valSet), len(getValSet)) for i := range getValSet { @@ -26,14 +31,22 @@ func FuzzEpochValSet(f *testing.F) { require.Equal(t, sdk.ValAddress(consAddr), getValSet[i].GetValAddress()) } + // at epoch 1 right now + epoch := keeper.GetEpoch(ctx) + require.Equal(t, uint64(1), epoch.EpochNumber) + params := keeper.GetParams(ctx) + // generate a random number of new blocks - numIncBlocks := r.Uint64()%1000 + 1 - for i := uint64(0); i < numIncBlocks; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + for i := uint64(0); i < params.EpochInterval; i++ { + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } + epoch = keeper.GetEpoch(ctx) + require.Equal(t, uint64(2), epoch.EpochNumber) + // check whether the validator set remains the same or not - getValSet2 := keeper.GetValidatorSet(ctx, keeper.GetEpoch(ctx).EpochNumber) + getValSet2 := keeper.GetValidatorSet(ctx, epoch.EpochNumber) require.Equal(t, len(valSet), len(getValSet2)) for i := range getValSet2 { consAddr, err := valSet[i].GetConsAddr() diff --git a/x/epoching/keeper/epochs.go b/x/epoching/keeper/epochs.go index 7e8cba969..48ff16606 100644 --- a/x/epoching/keeper/epochs.go +++ b/x/epoching/keeper/epochs.go @@ -1,43 +1,26 @@ package keeper import ( + "context" + "fmt" + errorsmod "cosmossdk.io/errors" - "github.com/babylonchain/babylon/x/epoching/types" + "cosmossdk.io/store/prefix" "github.com/cometbft/cometbft/crypto/merkle" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" -) -const ( - DefaultEpochNumber = 0 + "github.com/babylonchain/babylon/x/epoching/types" ) -// setEpochNumber sets epoch number -func (k Keeper) setEpochNumber(ctx sdk.Context, epochNumber uint64) { - store := ctx.KVStore(k.storeKey) - - epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) - store.Set(types.EpochNumberKey, epochNumberBytes) -} - -func (k Keeper) getEpochNumber(ctx sdk.Context) uint64 { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.EpochNumberKey) - if bz == nil { - panic(types.ErrUnknownEpochNumber) - } - epochNumber := sdk.BigEndianToUint64(bz) - return epochNumber -} - -func (k Keeper) setEpochInfo(ctx sdk.Context, epochNumber uint64, epoch *types.Epoch) { +func (k Keeper) setEpochInfo(ctx context.Context, epochNumber uint64, epoch *types.Epoch) { store := k.epochInfoStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) epochBytes := k.cdc.MustMarshal(epoch) store.Set(epochNumberBytes, epochBytes) } -func (k Keeper) getEpochInfo(ctx sdk.Context, epochNumber uint64) (*types.Epoch, error) { +func (k Keeper) getEpochInfo(ctx context.Context, epochNumber uint64) (*types.Epoch, error) { store := k.epochInfoStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) bz := store.Get(epochNumberBytes) @@ -50,65 +33,64 @@ func (k Keeper) getEpochInfo(ctx sdk.Context, epochNumber uint64) (*types.Epoch, } // InitEpoch sets the zero epoch number to DB -func (k Keeper) InitEpoch(ctx sdk.Context) { - header := ctx.BlockHeader() +func (k Keeper) InitEpoch(ctx context.Context) { + header := sdk.UnwrapSDKContext(ctx).HeaderInfo() if header.Height > 0 { panic("InitEpoch can be invoked only at genesis") } epochInterval := k.GetParams(ctx).EpochInterval - epoch := types.NewEpoch(0, epochInterval, 0, &header) + epoch := types.NewEpoch(0, epochInterval, 0, &header.Time) k.setEpochInfo(ctx, 0, &epoch) - - k.setEpochNumber(ctx, 0) } // GetEpoch fetches the current epoch -func (k Keeper) GetEpoch(ctx sdk.Context) *types.Epoch { - epochNumber := k.getEpochNumber(ctx) - epoch, err := k.getEpochInfo(ctx, epochNumber) - if err != nil { - panic(err) - } - return epoch +func (k Keeper) GetEpoch(ctx context.Context) *types.Epoch { + store := k.epochInfoStore(ctx) + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + epochBytes := iter.Value() + var epoch types.Epoch + k.cdc.MustUnmarshal(epochBytes, &epoch) + + return &epoch } -func (k Keeper) GetHistoricalEpoch(ctx sdk.Context, epochNumber uint64) (*types.Epoch, error) { +func (k Keeper) GetHistoricalEpoch(ctx context.Context, epochNumber uint64) (*types.Epoch, error) { epoch, err := k.getEpochInfo(ctx, epochNumber) return epoch, err } // RecordLastHeaderAndAppHashRoot records the last header and Merkle root of all AppHashs // for the current epoch, and stores the epoch metadata to KVStore -func (k Keeper) RecordLastHeaderAndAppHashRoot(ctx sdk.Context) error { +func (k Keeper) RecordLastHeaderAndAppHashRoot(ctx context.Context) error { epoch := k.GetEpoch(ctx) if !epoch.IsLastBlock(ctx) { return errorsmod.Wrapf(types.ErrInvalidHeight, "RecordLastBlockHeader can only be invoked at the last block of an epoch") } // record last block header - header := ctx.BlockHeader() - epoch.LastBlockHeader = &header + header := sdk.UnwrapSDKContext(ctx).HeaderInfo() + epoch.LastBlockTime = &header.Time // calculate and record the Merkle root - appHashs, err := k.GetAllAppHashsForEpoch(ctx, epoch) + appHashes, err := k.GetAllAppHashesForEpoch(ctx, epoch) if err != nil { return err } - appHashRoot := merkle.HashFromByteSlices(appHashs) + appHashRoot := merkle.HashFromByteSlices(appHashes) epoch.AppHashRoot = appHashRoot // save back to KVStore k.setEpochInfo(ctx, epoch.EpochNumber, epoch) return nil } -// RecordSealerHeaderForPrevEpoch records the sealer header for the previous epoch, -// where the sealer header of an epoch is the 2nd header of the next epoch -// This validator set of the epoch has generated a BLS multisig on `last_commit_hash` of the sealer header -func (k Keeper) RecordSealerHeaderForPrevEpoch(ctx sdk.Context) *types.Epoch { - // get the sealer header +// RecordSealerAppHashForPrevEpoch records the AppHash referencing +// the last block of the previous epoch +func (k Keeper) RecordSealerAppHashForPrevEpoch(ctx context.Context) *types.Epoch { epoch := k.GetEpoch(ctx) - if !epoch.IsSecondBlock(ctx) { - panic("RecordSealerHeaderForPrevEpoch can only be invoked at the second header of a non-zero epoch") + if !epoch.IsFirstBlock(ctx) { + panic(fmt.Errorf("RecordSealerAppHashForPrevEpoch can only be invoked at the first header of a non-zero epoch. "+ + "current epoch: %v, current height: %d", epoch, sdk.UnwrapSDKContext(ctx).HeaderInfo().Height)) } - header := ctx.BlockHeader() + header := sdk.UnwrapSDKContext(ctx).HeaderInfo() // get the sealed epoch, i.e., the epoch earlier than the current epoch sealedEpoch, err := k.GetHistoricalEpoch(ctx, epoch.EpochNumber-1) @@ -116,22 +98,40 @@ func (k Keeper) RecordSealerHeaderForPrevEpoch(ctx sdk.Context) *types.Epoch { panic(err) } - // record the sealer header for the sealed epoch - sealedEpoch.SealerHeader = &header + // record the sealer AppHash for the sealed epoch + sealedEpoch.SealerAppHash = header.AppHash k.setEpochInfo(ctx, sealedEpoch.EpochNumber, sealedEpoch) return sealedEpoch } +// RecordSealerBlockHashForEpoch records the block hash of +// the last block of the current epoch +func (k Keeper) RecordSealerBlockHashForEpoch(ctx context.Context) *types.Epoch { + // get the sealer header + epoch := k.GetEpoch(ctx) + if !epoch.IsLastBlock(ctx) { + panic(fmt.Errorf("RecordSealerBlockHashForEpoch can only be invoked at the last header of a non-zero epoch. "+ + "current epoch: %v, current height: %d", epoch, sdk.UnwrapSDKContext(ctx).HeaderInfo().Height)) + } + header := sdk.UnwrapSDKContext(ctx).HeaderInfo() + + // record the sealer block hash for the sealing epoch + epoch.SealerBlockHash = header.Hash + k.setEpochInfo(ctx, epoch.EpochNumber, epoch) + + return epoch +} + // IncEpoch adds epoch number by 1 // CONTRACT: can only be invoked at the first block of an epoch -func (k Keeper) IncEpoch(ctx sdk.Context) types.Epoch { +func (k Keeper) IncEpoch(ctx context.Context) types.Epoch { + sdkCtx := sdk.UnwrapSDKContext(ctx) epochNumber := k.GetEpoch(ctx).EpochNumber incrementedEpochNumber := epochNumber + 1 - k.setEpochNumber(ctx, incrementedEpochNumber) epochInterval := k.GetParams(ctx).EpochInterval - newEpoch := types.NewEpoch(incrementedEpochNumber, epochInterval, uint64(ctx.BlockHeight()), nil) + newEpoch := types.NewEpoch(incrementedEpochNumber, epochInterval, uint64(sdkCtx.HeaderInfo().Height), nil) k.setEpochInfo(ctx, incrementedEpochNumber, &newEpoch) return newEpoch @@ -141,7 +141,7 @@ func (k Keeper) IncEpoch(ctx sdk.Context) types.Epoch { // prefix: EpochInfoKey // key: epochNumber // value: epoch metadata -func (k Keeper) epochInfoStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.EpochInfoKey) +func (k Keeper) epochInfoStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.EpochInfoKey) } diff --git a/x/epoching/keeper/epochs_test.go b/x/epoching/keeper/epochs_test.go index 3a864a5ea..a4a3dff99 100644 --- a/x/epoching/keeper/epochs_test.go +++ b/x/epoching/keeper/epochs_test.go @@ -4,10 +4,10 @@ import ( "math/rand" "testing" - "github.com/babylonchain/babylon/testutil/datagen" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" ) func FuzzEpochs(f *testing.F) { @@ -16,33 +16,29 @@ func FuzzEpochs(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelper(t) - ctx, keeper := helper.Ctx, helper.EpochingKeeper + helper := testhelper.NewHelper(t) + ctx, keeper := helper.Ctx, helper.App.EpochingKeeper // ensure that the epoch info is correct at the genesis epoch := keeper.GetEpoch(ctx) - require.Equal(t, epoch.EpochNumber, uint64(0)) - require.Equal(t, epoch.FirstBlockHeight, uint64(0)) + require.Equal(t, epoch.EpochNumber, uint64(1)) + require.Equal(t, epoch.FirstBlockHeight, uint64(1)) // set a random epoch interval - epochInterval := r.Uint64()%100 + 2 // the epoch interval should at at least 2 - - params := types.Params{ - EpochInterval: epochInterval, - } - - if err := keeper.SetParams(ctx, params); err != nil { - panic(err) - } + epochInterval := keeper.GetParams(ctx).EpochInterval // increment a random number of new blocks numIncBlocks := r.Uint64()%1000 + 1 - for i := uint64(0); i < numIncBlocks; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + var err error + for i := uint64(0); i < numIncBlocks-1; i++ { + // TODO: Figure out why when ctx height is 1, ApplyEmptyBlockWithVoteExtension + // will still give ctx height 1 once, then start to increment + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } // ensure that the epoch info is still correct - expectedEpochNumber := numIncBlocks / epochInterval - if numIncBlocks%epochInterval > 0 { + expectedEpochNumber := (numIncBlocks + 1) / epochInterval + if (numIncBlocks+1)%epochInterval > 0 { expectedEpochNumber += 1 } actualNewEpoch := keeper.GetEpoch(ctx) diff --git a/x/epoching/keeper/grpc_query_test.go b/x/epoching/keeper/grpc_query_test.go index 6689b04ea..8e528da99 100644 --- a/x/epoching/keeper/grpc_query_test.go +++ b/x/epoching/keeper/grpc_query_test.go @@ -4,14 +4,15 @@ import ( "math/rand" "testing" + "cosmossdk.io/core/header" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/babylonchain/babylon/testutil/datagen" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + "github.com/babylonchain/babylon/x/epoching/types" ) // FuzzParamsQuery fuzzes queryClient.Params @@ -37,9 +38,8 @@ func FuzzParamsQuery(f *testing.F) { params.EpochInterval = uint64(r.Int()) } - helper := testepoching.NewHelper(t) - ctx, keeper, queryClient := helper.Ctx, helper.EpochingKeeper, helper.QueryClient - wctx := sdk.WrapSDKContext(ctx) + helper := testhelper.NewHelper(t) + ctx, keeper, queryClient := helper.Ctx, helper.App.EpochingKeeper, helper.QueryClient // if setParamsFlag == 0, set params setParamsFlag := r.Intn(2) if setParamsFlag == 0 { @@ -48,7 +48,7 @@ func FuzzParamsQuery(f *testing.F) { } } req := types.QueryParamsRequest{} - resp, err := queryClient.Params(wctx, &req) + resp, err := queryClient.Params(ctx, &req) require.NoError(t, err) // if setParamsFlag == 0, resp.Params should be changed, otherwise default if setParamsFlag == 0 { @@ -69,21 +69,27 @@ func FuzzCurrentEpoch(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - increment := datagen.RandomInt(r, 100) + increment := datagen.RandomInt(r, 100) + 1 - helper := testepoching.NewHelper(t) - ctx, keeper, queryClient := helper.Ctx, helper.EpochingKeeper, helper.QueryClient - wctx := sdk.WrapSDKContext(ctx) + helper := testhelper.NewHelper(t) + ctx, keeper, queryClient := helper.Ctx, helper.App.EpochingKeeper, helper.QueryClient epochInterval := keeper.GetParams(ctx).EpochInterval - for i := uint64(0); i < increment; i++ { + // starting from epoch 1 + for i := uint64(1); i < increment; i++ { // this ensures that IncEpoch is invoked only at the first header of each epoch - ctx = ctx.WithBlockHeader(*datagen.GenRandomTMHeader(r, "chain-test", i*epochInterval+1)) - wctx = sdk.WrapSDKContext(ctx) + randomHeader := datagen.GenRandomTMHeader(r, "chain-test", i*epochInterval+1) + headerInfo := header.Info{ + AppHash: randomHeader.AppHash, + Height: randomHeader.Height, + Time: randomHeader.Time, + ChainID: randomHeader.ChainID, + } + ctx = ctx.WithHeaderInfo(headerInfo) keeper.IncEpoch(ctx) } req := types.QueryCurrentEpochRequest{} - resp, err := queryClient.CurrentEpoch(wctx, &req) + resp, err := queryClient.CurrentEpoch(ctx, &req) require.NoError(t, err) require.Equal(t, increment, resp.CurrentEpoch) require.Equal(t, increment*epochInterval, resp.EpochBoundary) @@ -95,18 +101,19 @@ func FuzzEpochsInfo(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - numEpochs := datagen.RandomInt(r, 10) + 1 + var err error + numEpochs := datagen.RandomInt(r, 10) + 2 limit := datagen.RandomInt(r, 10) + 1 - helper := testepoching.NewHelper(t) - ctx, keeper, queryClient := helper.Ctx, helper.EpochingKeeper, helper.QueryClient - wctx := sdk.WrapSDKContext(ctx) + helper := testhelper.NewHelper(t) + ctx, keeper, queryClient := helper.Ctx, helper.App.EpochingKeeper, helper.QueryClient - // enque the first block of the numEpochs'th epoch + // enque the 1st block of the numEpochs'th epoch epochInterval := keeper.GetParams(ctx).EpochInterval - for i := uint64(0); i < numEpochs-1; i++ { + for i := uint64(0); i < (numEpochs - 2); i++ { // exclude the existing epoch 0 and 1 for j := uint64(0); j < epochInterval; j++ { - helper.GenAndApplyEmptyBlock(r) + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } } @@ -116,10 +123,10 @@ func FuzzEpochsInfo(f *testing.F) { Limit: limit, }, } - resp, err := queryClient.EpochsInfo(wctx, &req) + resp, err := queryClient.EpochsInfo(ctx, &req) require.NoError(t, err) - require.Equal(t, testepoching.Min(numEpochs, limit), uint64(len(resp.Epochs))) + require.Equal(t, min(numEpochs, limit), uint64(len(resp.Epochs))) for i, epoch := range resp.Epochs { require.Equal(t, uint64(i), epoch.EpochNumber) } @@ -139,9 +146,8 @@ func FuzzEpochMsgsQuery(f *testing.F) { limit := uint64(r.Int()%100) + 1 txidsMap := map[string]bool{} - helper := testepoching.NewHelper(t) - ctx, keeper, queryClient := helper.Ctx, helper.EpochingKeeper, helper.QueryClient - wctx := sdk.WrapSDKContext(ctx) + helper := testhelper.NewHelper(t) + ctx, keeper, queryClient := helper.Ctx, helper.App.EpochingKeeper, helper.QueryClient // enque a random number of msgs with random txids for i := uint64(0); i < numMsgs; i++ { txid := datagen.GenRandomByteArray(r, 32) @@ -154,15 +160,15 @@ func FuzzEpochMsgsQuery(f *testing.F) { } // get epoch msgs req := types.QueryEpochMsgsRequest{ - EpochNum: 0, + EpochNum: 1, Pagination: &query.PageRequest{ Limit: limit, }, } - resp, err := queryClient.EpochMsgs(wctx, &req) + resp, err := queryClient.EpochMsgs(ctx, &req) require.NoError(t, err) - require.Equal(t, testepoching.Min(uint64(len(txidsMap)), limit), uint64(len(resp.Msgs))) + require.Equal(t, min(uint64(len(txidsMap)), limit), uint64(len(resp.Msgs))) for idx := range resp.Msgs { _, ok := txidsMap[string(resp.Msgs[idx].TxId)] require.True(t, ok) @@ -170,12 +176,12 @@ func FuzzEpochMsgsQuery(f *testing.F) { // epoch 1 is out of scope req = types.QueryEpochMsgsRequest{ - EpochNum: 1, + EpochNum: 2, Pagination: &query.PageRequest{ Limit: limit, }, } - _, err = queryClient.EpochMsgs(wctx, &req) + _, err = queryClient.EpochMsgs(ctx, &req) require.Error(t, err) }) } @@ -188,12 +194,15 @@ func FuzzEpochValSetQuery(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - helper := testepoching.NewHelperWithValSet(t) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + helper := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) ctx, queryClient := helper.Ctx, helper.QueryClient limit := uint64(r.Int() % 100) req := &types.QueryEpochValSetRequest{ - EpochNum: 0, + EpochNum: 1, Pagination: &query.PageRequest{ Limit: limit, }, @@ -202,10 +211,12 @@ func FuzzEpochValSetQuery(f *testing.F) { resp, err := queryClient.EpochValSet(ctx, req) require.NoError(t, err) + params := helper.App.EpochingKeeper.GetParams(ctx) + // generate a random number of new blocks - numIncBlocks := r.Uint64()%1000 + 1 - for i := uint64(0); i < numIncBlocks; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) + for i := uint64(0); i < params.EpochInterval; i++ { + ctx, err = helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) } // check whether the validator set remains the same or not diff --git a/x/epoching/keeper/hooks.go b/x/epoching/keeper/hooks.go index a30ab69e6..46cb2a758 100644 --- a/x/epoching/keeper/hooks.go +++ b/x/epoching/keeper/hooks.go @@ -1,6 +1,9 @@ package keeper import ( + "context" + + "cosmossdk.io/math" checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" "github.com/babylonchain/babylon/x/epoching/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,21 +14,21 @@ import ( var _ types.EpochingHooks = Keeper{} // AfterEpochBegins - call hook if registered -func (k Keeper) AfterEpochBegins(ctx sdk.Context, epoch uint64) { +func (k Keeper) AfterEpochBegins(ctx context.Context, epoch uint64) { if k.hooks != nil { k.hooks.AfterEpochBegins(ctx, epoch) } } // AfterEpochEnds - call hook if registered -func (k Keeper) AfterEpochEnds(ctx sdk.Context, epoch uint64) { +func (k Keeper) AfterEpochEnds(ctx context.Context, epoch uint64) { if k.hooks != nil { k.hooks.AfterEpochEnds(ctx, epoch) } } // BeforeSlashThreshold triggers the BeforeSlashThreshold hook for other modules that register this hook -func (k Keeper) BeforeSlashThreshold(ctx sdk.Context, valSet types.ValidatorSet) { +func (k Keeper) BeforeSlashThreshold(ctx context.Context, valSet types.ValidatorSet) { if k.hooks != nil { k.hooks.BeforeSlashThreshold(ctx, valSet) } @@ -44,7 +47,8 @@ var _ checkpointingtypes.CheckpointingHooks = Hooks{} func (k Keeper) Hooks() Hooks { return Hooks{k} } // BeforeValidatorSlashed records the slash event -func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error { +func (h Hooks) BeforeValidatorSlashed(ctx context.Context, valAddr sdk.ValAddress, fraction math.LegacyDec) error { + sdkCtx := sdk.UnwrapSDKContext(ctx) thresholds := []float64{float64(1) / float64(3), float64(2) / float64(3)} epochNumber := h.k.GetEpoch(ctx).EpochNumber @@ -68,7 +72,7 @@ func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, f slashedVals := h.k.GetSlashedValidators(ctx, epochNumber) slashedVals = append(slashedVals, thisVal) event := types.NewEventSlashThreshold(slashedVotingPower, totalVotingPower, slashedVals) - if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { + if err := sdkCtx.EventManager().EmitTypedEvent(&event); err != nil { panic(err) } h.k.BeforeSlashThreshold(ctx, slashedVals) @@ -84,57 +88,59 @@ func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, f return nil } -func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { - return h.k.RecordNewValState(ctx, valAddr, types.BondState_CREATED) +func (h Hooks) AfterValidatorCreated(ctx context.Context, valAddr sdk.ValAddress) error { + return h.k.RecordNewValState(sdk.UnwrapSDKContext(ctx), valAddr, types.BondState_CREATED) } -func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { - return h.k.RecordNewValState(ctx, valAddr, types.BondState_REMOVED) +func (h Hooks) AfterValidatorRemoved(ctx context.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { + return h.k.RecordNewValState(sdk.UnwrapSDKContext(ctx), valAddr, types.BondState_REMOVED) } -func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { - return h.k.RecordNewValState(ctx, valAddr, types.BondState_BONDED) +func (h Hooks) AfterValidatorBonded(ctx context.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { + return h.k.RecordNewValState(sdk.UnwrapSDKContext(ctx), valAddr, types.BondState_BONDED) } -func (h Hooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { - return h.k.RecordNewValState(ctx, valAddr, types.BondState_UNBONDING) +func (h Hooks) AfterValidatorBeginUnbonding(ctx context.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { + return h.k.RecordNewValState(sdk.UnwrapSDKContext(ctx), valAddr, types.BondState_UNBONDING) } -func (h Hooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { - return h.k.RecordNewDelegationState(ctx, delAddr, valAddr, types.BondState_REMOVED) +func (h Hooks) BeforeDelegationRemoved(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { + return h.k.RecordNewDelegationState(sdk.UnwrapSDKContext(ctx), delAddr, valAddr, nil, types.BondState_REMOVED) } -func (h Hooks) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { - // finalise all unbonding validators/delegations in this epoch - h.k.ApplyMatureUnbonding(ctx, epoch) +func (h Hooks) AfterUnbondingInitiated(ctx context.Context, id uint64) error { return nil } // Other hooks that are not used in the epoching module -func (h Hooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error { +func (h Hooks) BeforeValidatorModified(ctx context.Context, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { +func (h Hooks) BeforeDelegationCreated(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { +func (h Hooks) BeforeDelegationSharesModified(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { +func (h Hooks) AfterDelegationModified(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error { return nil } - -func (h Hooks) AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error { return nil } -func (h Hooks) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +// Checkpointing hooks +func (h Hooks) AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error { + // finalise all unbonding validators/delegations in this epoch + h.k.ApplyMatureUnbonding(ctx, epoch) return nil } -func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +func (h Hooks) AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error { return nil } + +func (h Hooks) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { return nil } + +func (h Hooks) AfterRawCheckpointForgotten(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return nil } -func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { +func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return nil } diff --git a/x/epoching/keeper/keeper.go b/x/epoching/keeper/keeper.go index dbe78f4f9..11741f489 100644 --- a/x/epoching/keeper/keeper.go +++ b/x/epoching/keeper/keeper.go @@ -2,25 +2,23 @@ package keeper import ( "fmt" - - "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + corestoretypes "cosmossdk.io/core/store" + "cosmossdk.io/log" "github.com/babylonchain/babylon/x/epoching/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" ) type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - hooks types.EpochingHooks - bk types.BankKeeper - stk types.StakingKeeper - router *baseapp.MsgServiceRouter + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService + hooks types.EpochingHooks + bk types.BankKeeper + stk types.StakingKeeper + router *baseapp.MsgServiceRouter // the address capable of executing a MsgUpdateParams message. Typically, this // should be the x/gov module account. authority string @@ -29,21 +27,19 @@ type ( func NewKeeper( cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, bk types.BankKeeper, stk types.StakingKeeper, authority string, ) Keeper { return Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - hooks: nil, - bk: bk, - stk: stk, - authority: authority, + cdc: cdc, + storeService: storeService, + hooks: nil, + bk: bk, + stk: stk, + authority: authority, } } diff --git a/x/epoching/keeper/keeper_test.go b/x/epoching/keeper/keeper_test.go index 64a9aae1c..80ea94d3b 100644 --- a/x/epoching/keeper/keeper_test.go +++ b/x/epoching/keeper/keeper_test.go @@ -5,22 +5,22 @@ import ( "github.com/stretchr/testify/require" - "github.com/babylonchain/babylon/x/epoching/testepoching" + testhelper "github.com/babylonchain/babylon/testutil/helper" "github.com/babylonchain/babylon/x/epoching/types" ) func TestParams(t *testing.T) { - helper := testepoching.NewHelper(t) - keeper := helper.EpochingKeeper + helper := testhelper.NewHelper(t) + keeper := helper.App.EpochingKeeper ctx := helper.Ctx expParams := types.DefaultParams() - //check that the empty keeper loads the default - resParams := helper.EpochingKeeper.GetParams(ctx) + // check that the empty keeper loads the default + resParams := helper.App.EpochingKeeper.GetParams(ctx) require.True(t, expParams.Equal(resParams)) - //modify a params, save, and retrieve + // modify a params, save, and retrieve expParams.EpochInterval = 777 if err := keeper.SetParams(ctx, expParams); err != nil { diff --git a/x/epoching/keeper/lifecycle_delegation.go b/x/epoching/keeper/lifecycle_delegation.go index 1e5896ba5..d00859908 100644 --- a/x/epoching/keeper/lifecycle_delegation.go +++ b/x/epoching/keeper/lifecycle_delegation.go @@ -1,15 +1,18 @@ package keeper import ( + "context" + + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) // TODO: add more tests on the lifecycle record // RecordNewDelegationState adds a state for a delegation lifecycle, including created, bonded, unbonding and unbonded -func (k Keeper) RecordNewDelegationState(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, state types.BondState) error { +func (k Keeper) RecordNewDelegationState(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount *sdk.Coin, state types.BondState) error { lc := k.GetDelegationLifecycle(ctx, delAddr) if lc == nil { lc = &types.DelegationLifecycle{ @@ -17,10 +20,12 @@ func (k Keeper) RecordNewDelegationState(ctx sdk.Context, delAddr sdk.AccAddress DelLife: []*types.DelegationStateUpdate{}, } } - height, time := ctx.BlockHeight(), ctx.BlockTime() + sdkCtx := sdk.UnwrapSDKContext(ctx) + height, time := sdkCtx.HeaderInfo().Height, sdkCtx.HeaderInfo().Time DelegationStateUpdate := types.DelegationStateUpdate{ State: state, ValAddr: valAddr.String(), + Amount: amount, BlockHeight: uint64(height), BlockTime: &time, } @@ -29,15 +34,15 @@ func (k Keeper) RecordNewDelegationState(ctx sdk.Context, delAddr sdk.AccAddress return nil } -func (k Keeper) SetDelegationLifecycle(ctx sdk.Context, delAddr sdk.AccAddress, lc *types.DelegationLifecycle) { +func (k Keeper) SetDelegationLifecycle(ctx context.Context, delAddr sdk.AccAddress, lc *types.DelegationLifecycle) { store := k.delegationLifecycleStore(ctx) lcBytes := k.cdc.MustMarshal(lc) - store.Set([]byte(delAddr), lcBytes) + store.Set(delAddr, lcBytes) } -func (k Keeper) GetDelegationLifecycle(ctx sdk.Context, delAddr sdk.AccAddress) *types.DelegationLifecycle { +func (k Keeper) GetDelegationLifecycle(ctx context.Context, delAddr sdk.AccAddress) *types.DelegationLifecycle { store := k.delegationLifecycleStore(ctx) - lcBytes := store.Get([]byte(delAddr)) + lcBytes := store.Get(delAddr) if len(lcBytes) == 0 { return nil } @@ -50,7 +55,7 @@ func (k Keeper) GetDelegationLifecycle(ctx sdk.Context, delAddr sdk.AccAddress) // prefix: DelegationLifecycleKey // key: del_addr // value: DelegationLifecycle object -func (k Keeper) delegationLifecycleStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.DelegationLifecycleKey) +func (k Keeper) delegationLifecycleStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.DelegationLifecycleKey) } diff --git a/x/epoching/keeper/lifecycle_validator.go b/x/epoching/keeper/lifecycle_validator.go index 637f69810..75f6250bf 100644 --- a/x/epoching/keeper/lifecycle_validator.go +++ b/x/epoching/keeper/lifecycle_validator.go @@ -1,8 +1,11 @@ package keeper import ( + "context" + + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -17,7 +20,7 @@ func (k Keeper) RecordNewValState(ctx sdk.Context, valAddr sdk.ValAddress, state ValLife: []*types.ValStateUpdate{}, } } - height, time := ctx.BlockHeight(), ctx.BlockTime() + height, time := ctx.HeaderInfo().Height, ctx.HeaderInfo().Time valStateUpdate := types.ValStateUpdate{ State: state, BlockHeight: uint64(height), @@ -28,15 +31,15 @@ func (k Keeper) RecordNewValState(ctx sdk.Context, valAddr sdk.ValAddress, state return nil } -func (k Keeper) SetValLifecycle(ctx sdk.Context, valAddr sdk.ValAddress, lc *types.ValidatorLifecycle) { +func (k Keeper) SetValLifecycle(ctx context.Context, valAddr sdk.ValAddress, lc *types.ValidatorLifecycle) { store := k.valLifecycleStore(ctx) lcBytes := k.cdc.MustMarshal(lc) - store.Set([]byte(valAddr), lcBytes) + store.Set(valAddr, lcBytes) } -func (k Keeper) GetValLifecycle(ctx sdk.Context, valAddr sdk.ValAddress) *types.ValidatorLifecycle { +func (k Keeper) GetValLifecycle(ctx context.Context, valAddr sdk.ValAddress) *types.ValidatorLifecycle { store := k.valLifecycleStore(ctx) - lcBytes := store.Get([]byte(valAddr)) + lcBytes := store.Get(valAddr) if len(lcBytes) == 0 { return nil } @@ -49,7 +52,7 @@ func (k Keeper) GetValLifecycle(ctx sdk.Context, valAddr sdk.ValAddress) *types. // prefix: ValidatorLifecycleKey // key: val_addr // value: ValidatorLifecycle object -func (k Keeper) valLifecycleStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.ValidatorLifecycleKey) +func (k Keeper) valLifecycleStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.ValidatorLifecycleKey) } diff --git a/x/epoching/keeper/modified_staking.go b/x/epoching/keeper/modified_staking.go index b68b1c22f..44c193494 100644 --- a/x/epoching/keeper/modified_staking.go +++ b/x/epoching/keeper/modified_staking.go @@ -1,6 +1,7 @@ package keeper import ( + "context" "fmt" "github.com/babylonchain/babylon/x/epoching/types" @@ -17,31 +18,46 @@ import ( // - an unbonding/redelegation becomes mature when its corresponding epoch and all previous epochs have been checkpointed. // Triggered by the checkpointing module upon the above condition. // (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/x/staking/keeper/val_state_change.go#L32-L91) -func (k Keeper) ApplyMatureUnbonding(ctx sdk.Context, epochNumber uint64) { +func (k Keeper) ApplyMatureUnbonding(ctx context.Context, epochNumber uint64) { + sdkCtx := sdk.UnwrapSDKContext(ctx) // save the current ctx for emitting events and recording lifecycle - currentCtx := ctx + currentSdkCtx := sdkCtx // get the ctx of the last block of the given epoch, while offsetting the time to nullify UnbondingTime finalizedEpoch, err := k.GetHistoricalEpoch(ctx, epochNumber) if err != nil { panic(err) } - epochBoundaryHeader := finalizedEpoch.LastBlockHeader - epochBoundaryHeader.Time = epochBoundaryHeader.Time.Add(k.stk.GetParams(ctx).UnbondingTime) // nullifies the effect of UnbondingTime in staking module - ctx = ctx.WithBlockHeader(*epochBoundaryHeader) + params, err := k.stk.GetParams(ctx) + if err != nil { + panic(err) + } + // nullifies the effect of UnbondingTime in staking module + // NOTE: we offset time in both Header and HeaderInfo for full compatibility + finalizedTime := finalizedEpoch.LastBlockTime.Add(params.UnbondingTime) + headerInfo := sdkCtx.HeaderInfo() + headerInfo.Time = finalizedTime + ctx = sdkCtx.WithBlockTime(finalizedTime).WithHeaderInfo(headerInfo) // unbond all mature validators till the last block of the given epoch - matureValidators := k.getAllMatureValidators(ctx) - currentCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following unbonding validators matured in epoch %d: %v", epochNumber, matureValidators)) - k.stk.UnbondAllMatureValidators(ctx) + matureValidators := k.getAllMatureValidators(sdkCtx) + currentSdkCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following unbonding validators matured in epoch %d: %v", epochNumber, matureValidators)) + if err := k.stk.UnbondAllMatureValidators(ctx); err != nil { + panic(err) + } // record state update of being UNBONDED for mature validators for _, valAddr := range matureValidators { - k.RecordNewValState(currentCtx, valAddr, types.BondState_UNBONDED) //nolint:errcheck // either we ignore the error here, or propoagate up the stack + if err := k.RecordNewValState(currentSdkCtx, valAddr, types.BondState_UNBONDED); err != nil { + panic(err) + } } // get all mature unbonding delegations the epoch boundary from the ubd queue. - matureUnbonds := k.stk.DequeueAllMatureUBDQueue(ctx, epochBoundaryHeader.Time) - currentCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following unbonding delegations matured in epoch %d: %v", epochNumber, matureUnbonds)) + matureUnbonds, err := k.stk.DequeueAllMatureUBDQueue(ctx, finalizedTime) + if err != nil { + panic(err) + } + currentSdkCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following unbonding delegations matured in epoch %d: %v", epochNumber, matureUnbonds)) // unbond all mature delegations for _, dvPair := range matureUnbonds { @@ -60,9 +76,12 @@ func (k Keeper) ApplyMatureUnbonding(ctx sdk.Context, epochNumber uint64) { // Babylon modification: record delegation state // AFTER mature, unbonded from the validator - k.RecordNewDelegationState(currentCtx, delAddr, valAddr, types.BondState_UNBONDED) //nolint:errcheck // either we ignore the error here, or propoagate up the stack + // TODO: find a way to specify amount? + if err := k.RecordNewDelegationState(currentSdkCtx, delAddr, valAddr, nil, types.BondState_UNBONDED); err != nil { + panic(err) + } - currentCtx.EventManager().EmitEvent( + currentSdkCtx.EventManager().EmitEvent( sdk.NewEvent( stakingtypes.EventTypeCompleteUnbonding, sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()), @@ -73,8 +92,11 @@ func (k Keeper) ApplyMatureUnbonding(ctx sdk.Context, epochNumber uint64) { } // get all mature redelegations till the epoch boundary from the red queue. - matureRedelegations := k.stk.DequeueAllMatureRedelegationQueue(ctx, epochBoundaryHeader.Time) - currentCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following redelegations matured in epoch %d: %v", epochNumber, matureRedelegations)) + matureRedelegations, err := k.stk.DequeueAllMatureRedelegationQueue(ctx, finalizedTime) + if err != nil { + panic(err) + } + currentSdkCtx.Logger().Info(fmt.Sprintf("Epoching: start completing the following redelegations matured in epoch %d: %v", epochNumber, matureRedelegations)) // finish all mature redelegations for _, dvvTriplet := range matureRedelegations { @@ -102,11 +124,18 @@ func (k Keeper) ApplyMatureUnbonding(ctx sdk.Context, epochNumber uint64) { // Babylon modification: record delegation state // AFTER mature, unbonded from the source validator, created/bonded to the destination validator - k.RecordNewDelegationState(currentCtx, delAddr, valSrcAddr, types.BondState_UNBONDED) //nolint:errcheck // either we ignore the error here, or propoagate up the stack - k.RecordNewDelegationState(currentCtx, delAddr, valDstAddr, types.BondState_CREATED) //nolint:errcheck // either we ignore the error here, or propoagate up the stack - k.RecordNewDelegationState(currentCtx, delAddr, valDstAddr, types.BondState_BONDED) //nolint:errcheck // either we ignore the error here, or propoagate up the stack + // TODO: find a way to specify amount? + if err := k.RecordNewDelegationState(currentSdkCtx, delAddr, valSrcAddr, nil, types.BondState_UNBONDED); err != nil { + panic(err) + } + if err := k.RecordNewDelegationState(currentSdkCtx, delAddr, valDstAddr, nil, types.BondState_CREATED); err != nil { + panic(err) + } + if err := k.RecordNewDelegationState(currentSdkCtx, delAddr, valDstAddr, nil, types.BondState_BONDED); err != nil { + panic(err) + } - currentCtx.EventManager().EmitEvent( + currentSdkCtx.EventManager().EmitEvent( sdk.NewEvent( stakingtypes.EventTypeCompleteRedelegation, sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()), @@ -126,7 +155,7 @@ func (k Keeper) ApplyMatureUnbonding(ctx sdk.Context, epochNumber uint64) { // * Updates relevant indices. // Triggered upon every epoch. // (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/x/staking/keeper/val_state_change.go#L18-L30) -func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) []abci.ValidatorUpdate { +func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx context.Context) []abci.ValidatorUpdate { validatorUpdates, err := k.stk.ApplyAndReturnValidatorSetUpdates(ctx) if err != nil { panic(err) @@ -140,15 +169,18 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) []abci.Valida func (k Keeper) getAllMatureValidators(ctx sdk.Context) []sdk.ValAddress { matureValAddrs := []sdk.ValAddress{} - blockTime := ctx.BlockTime() - blockHeight := ctx.BlockHeight() + blockTime := ctx.HeaderInfo().Time + blockHeight := ctx.HeaderInfo().Height // unbondingValIterator will contains all validator addresses indexed under // the ValidatorQueueKey prefix. Note, the entire index key is composed as // ValidatorQueueKey | timeBzLen (8-byte big endian) | timeBz | heightBz (8-byte big endian), // so it may be possible that certain validator addresses that are iterated // over are not ready to unbond, so an explicit check is required. - unbondingValIterator := k.stk.ValidatorQueueIterator(ctx, blockTime, blockHeight) + unbondingValIterator, err := k.stk.ValidatorQueueIterator(ctx, blockTime, blockHeight) + if err != nil { + panic(fmt.Errorf("could not get iterator to validator's queue: %s", err)) + } defer unbondingValIterator.Close() for ; unbondingValIterator.Valid(); unbondingValIterator.Next() { @@ -167,9 +199,9 @@ func (k Keeper) getAllMatureValidators(ctx sdk.Context) []sdk.ValAddress { if err != nil { panic(err) } - val, found := k.stk.GetValidator(ctx, addr) - if !found { - panic("validator in the unbonding queue was not found") + val, err := k.stk.GetValidator(ctx, addr) + if err != nil { + panic(err) } if !val.IsUnbonding() { diff --git a/x/epoching/keeper/msg_server.go b/x/epoching/keeper/msg_server.go index 17c2e1dec..3db5b554d 100644 --- a/x/epoching/keeper/msg_server.go +++ b/x/epoching/keeper/msg_server.go @@ -9,7 +9,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) type msgServer struct { @@ -25,32 +24,38 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} // WrappedDelegate handles the MsgWrappedDelegate request -func (k msgServer) WrappedDelegate(goCtx context.Context, msg *types.MsgWrappedDelegate) (*types.MsgWrappedDelegateResponse, error) { +func (ms msgServer) WrappedDelegate(goCtx context.Context, msg *types.MsgWrappedDelegate) (*types.MsgWrappedDelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + if msg.Msg == nil { + return nil, types.ErrNoWrappedMsg + } // verification rules ported from staking module valAddr, valErr := sdk.ValAddressFromBech32(msg.Msg.ValidatorAddress) if valErr != nil { return nil, valErr } - if _, found := k.stk.GetValidator(ctx, valAddr); !found { - return nil, stakingtypes.ErrNoValidatorFound + if _, err := ms.stk.GetValidator(ctx, valAddr); err != nil { + return nil, err } if _, err := sdk.AccAddressFromBech32(msg.Msg.DelegatorAddress); err != nil { return nil, err } - bondDenom := k.stk.BondDenom(ctx) + bondDenom, err := ms.stk.BondDenom(ctx) + if err != nil { + return nil, err + } if msg.Msg.Amount.Denom != bondDenom { return nil, errorsmod.Wrapf( sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Msg.Amount.Denom, bondDenom, ) } - blockHeight := uint64(ctx.BlockHeight()) + blockHeight := uint64(ctx.HeaderInfo().Height) if blockHeight == 0 { return nil, types.ErrZeroEpochMsg } - blockTime := ctx.BlockTime() + blockTime := ctx.HeaderInfo().Time txid := tmhash.Sum(ctx.TxBytes()) queuedMsg, err := types.NewQueuedMessage(blockHeight, blockTime, txid, msg) @@ -58,7 +63,7 @@ func (k msgServer) WrappedDelegate(goCtx context.Context, msg *types.MsgWrappedD return nil, err } - k.EnqueueMsg(ctx, queuedMsg) + ms.EnqueueMsg(ctx, queuedMsg) err = ctx.EventManager().EmitTypedEvents( &types.EventWrappedDelegate{ @@ -66,7 +71,7 @@ func (k msgServer) WrappedDelegate(goCtx context.Context, msg *types.MsgWrappedD ValidatorAddress: msg.Msg.ValidatorAddress, Amount: msg.Msg.Amount.Amount.Uint64(), Denom: msg.Msg.Amount.GetDenom(), - EpochBoundary: k.GetEpoch(ctx).GetLastBlockHeight(), + EpochBoundary: ms.GetEpoch(ctx).GetLastBlockHeight(), }, ) if err != nil { @@ -77,8 +82,11 @@ func (k msgServer) WrappedDelegate(goCtx context.Context, msg *types.MsgWrappedD } // WrappedUndelegate handles the MsgWrappedUndelegate request -func (k msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappedUndelegate) (*types.MsgWrappedUndelegateResponse, error) { +func (ms msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappedUndelegate) (*types.MsgWrappedUndelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + if msg.Msg == nil { + return nil, types.ErrNoWrappedMsg + } // verification rules ported from staking module valAddr, err := sdk.ValAddressFromBech32(msg.Msg.ValidatorAddress) @@ -89,21 +97,24 @@ func (k msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappe if err != nil { return nil, err } - if _, err := k.stk.ValidateUnbondAmount(ctx, delegatorAddress, valAddr, msg.Msg.Amount.Amount); err != nil { + if _, err := ms.stk.ValidateUnbondAmount(ctx, delegatorAddress, valAddr, msg.Msg.Amount.Amount); err != nil { + return nil, err + } + bondDenom, err := ms.stk.BondDenom(ctx) + if err != nil { return nil, err } - bondDenom := k.stk.BondDenom(ctx) if msg.Msg.Amount.Denom != bondDenom { return nil, errorsmod.Wrapf( sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Msg.Amount.Denom, bondDenom, ) } - blockHeight := uint64(ctx.BlockHeight()) + blockHeight := uint64(ctx.HeaderInfo().Height) if blockHeight == 0 { return nil, types.ErrZeroEpochMsg } - blockTime := ctx.BlockTime() + blockTime := ctx.HeaderInfo().Time txid := tmhash.Sum(ctx.TxBytes()) queuedMsg, err := types.NewQueuedMessage(blockHeight, blockTime, txid, msg) @@ -111,7 +122,7 @@ func (k msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappe return nil, err } - k.EnqueueMsg(ctx, queuedMsg) + ms.EnqueueMsg(ctx, queuedMsg) err = ctx.EventManager().EmitTypedEvents( &types.EventWrappedUndelegate{ @@ -119,7 +130,7 @@ func (k msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappe ValidatorAddress: msg.Msg.ValidatorAddress, Amount: msg.Msg.Amount.Amount.Uint64(), Denom: msg.Msg.Amount.GetDenom(), - EpochBoundary: k.GetEpoch(ctx).GetLastBlockHeight(), + EpochBoundary: ms.GetEpoch(ctx).GetLastBlockHeight(), }, ) if err != nil { @@ -130,8 +141,11 @@ func (k msgServer) WrappedUndelegate(goCtx context.Context, msg *types.MsgWrappe } // WrappedBeginRedelegate handles the MsgWrappedBeginRedelegate request -func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgWrappedBeginRedelegate) (*types.MsgWrappedBeginRedelegateResponse, error) { +func (ms msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgWrappedBeginRedelegate) (*types.MsgWrappedBeginRedelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + if msg.Msg == nil { + return nil, types.ErrNoWrappedMsg + } // verification rules ported from staking module valSrcAddr, err := sdk.ValAddressFromBech32(msg.Msg.ValidatorSrcAddress) @@ -142,10 +156,13 @@ func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgW if err != nil { return nil, err } - if _, err := k.stk.ValidateUnbondAmount(ctx, delegatorAddress, valSrcAddr, msg.Msg.Amount.Amount); err != nil { + if _, err := ms.stk.ValidateUnbondAmount(ctx, delegatorAddress, valSrcAddr, msg.Msg.Amount.Amount); err != nil { + return nil, err + } + bondDenom, err := ms.stk.BondDenom(ctx) + if err != nil { return nil, err } - bondDenom := k.stk.BondDenom(ctx) if msg.Msg.Amount.Denom != bondDenom { return nil, errorsmod.Wrapf( sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Msg.Amount.Denom, bondDenom, @@ -155,11 +172,11 @@ func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgW return nil, err } - blockHeight := uint64(ctx.BlockHeight()) + blockHeight := uint64(ctx.HeaderInfo().Height) if blockHeight == 0 { return nil, types.ErrZeroEpochMsg } - blockTime := ctx.BlockTime() + blockTime := ctx.HeaderInfo().Time txid := tmhash.Sum(ctx.TxBytes()) queuedMsg, err := types.NewQueuedMessage(blockHeight, blockTime, txid, msg) @@ -167,7 +184,7 @@ func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgW return nil, err } - k.EnqueueMsg(ctx, queuedMsg) + ms.EnqueueMsg(ctx, queuedMsg) err = ctx.EventManager().EmitTypedEvents( &types.EventWrappedBeginRedelegate{ DelegatorAddress: msg.Msg.DelegatorAddress, @@ -175,7 +192,7 @@ func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgW DestinationValidatorAddress: msg.Msg.ValidatorDstAddress, Amount: msg.Msg.Amount.Amount.Uint64(), Denom: msg.Msg.Amount.GetDenom(), - EpochBoundary: k.GetEpoch(ctx).GetLastBlockHeight(), + EpochBoundary: ms.GetEpoch(ctx).GetLastBlockHeight(), }, ) if err != nil { @@ -185,6 +202,74 @@ func (k msgServer) WrappedBeginRedelegate(goCtx context.Context, msg *types.MsgW return &types.MsgWrappedBeginRedelegateResponse{}, nil } +// WrappedCancelUnbondingDelegation handles the MsgWrappedCancelUnbondingDelegation request +func (ms msgServer) WrappedCancelUnbondingDelegation(goCtx context.Context, msg *types.MsgWrappedCancelUnbondingDelegation) (*types.MsgWrappedCancelUnbondingDelegationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + if msg.Msg == nil { + return nil, types.ErrNoWrappedMsg + } + + // verification rules ported from staking module + if _, err := sdk.AccAddressFromBech32(msg.Msg.DelegatorAddress); err != nil { + return nil, err + } + + if _, err := sdk.ValAddressFromBech32(msg.Msg.ValidatorAddress); err != nil { + return nil, err + } + + if !msg.Msg.Amount.IsValid() || !msg.Msg.Amount.Amount.IsPositive() { + return nil, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "invalid amount", + ) + } + + if msg.Msg.CreationHeight <= 0 { + return nil, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "invalid height", + ) + } + + bondDenom, err := ms.stk.BondDenom(ctx) + if err != nil { + return nil, err + } + if msg.Msg.Amount.Denom != bondDenom { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Msg.Amount.Denom, bondDenom, + ) + } + + blockHeight := uint64(ctx.BlockHeader().Height) + if blockHeight == 0 { + return nil, types.ErrZeroEpochMsg + } + blockTime := ctx.BlockTime() + txid := tmhash.Sum(ctx.TxBytes()) + queuedMsg, err := types.NewQueuedMessage(blockHeight, blockTime, txid, msg) + if err != nil { + return nil, err + } + + ms.EnqueueMsg(ctx, queuedMsg) + err = ctx.EventManager().EmitTypedEvents( + &types.EventWrappedCancelUnbondingDelegation{ + DelegatorAddress: msg.Msg.DelegatorAddress, + ValidatorAddress: msg.Msg.ValidatorAddress, + Amount: msg.Msg.Amount.Amount.Uint64(), + CreationHeight: msg.Msg.CreationHeight, + EpochBoundary: ms.GetEpoch(ctx).GetLastBlockHeight(), + }, + ) + if err != nil { + return nil, err + } + + return &types.MsgWrappedCancelUnbondingDelegationResponse{}, nil +} + // 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 @@ -193,6 +278,9 @@ func (ms msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdatePara if ms.authority != req.Authority { return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, req.Authority) } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } ctx := sdk.UnwrapSDKContext(goCtx) if err := ms.SetParams(ctx, req.Params); err != nil { diff --git a/x/epoching/keeper/msg_server_test.go b/x/epoching/keeper/msg_server_test.go index 46057efe5..cd1857f9f 100644 --- a/x/epoching/keeper/msg_server_test.go +++ b/x/epoching/keeper/msg_server_test.go @@ -5,21 +5,21 @@ import ( "testing" "time" - "github.com/babylonchain/babylon/x/epoching/testepoching" - "github.com/babylonchain/babylon/x/epoching/types" - sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" + + testhelper "github.com/babylonchain/babylon/testutil/helper" + "github.com/babylonchain/babylon/x/epoching/types" ) // TODO (fuzz tests): replace the following tests with fuzz ones func TestMsgWrappedDelegate(t *testing.T) { r := rand.New(rand.NewSource(time.Now().Unix())) - helper := testepoching.NewHelper(t) + helper := testhelper.NewHelper(t) msgSrvr := helper.MsgSrvr // enter 1st epoch, in which BBN starts handling validator-related msgs - ctx := helper.GenAndApplyEmptyBlock(r) - wctx := sdk.WrapSDKContext(ctx) + ctx, err := helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) testCases := []struct { name string @@ -34,7 +34,7 @@ func TestMsgWrappedDelegate(t *testing.T) { } for _, tc := range testCases { wrappedMsg := types.NewMsgWrappedDelegate(tc.req) - _, err := msgSrvr.WrappedDelegate(wctx, wrappedMsg) + _, err := msgSrvr.WrappedDelegate(ctx, wrappedMsg) if tc.expectErr { require.Error(t, err) } else { @@ -45,11 +45,11 @@ func TestMsgWrappedDelegate(t *testing.T) { func TestMsgWrappedUndelegate(t *testing.T) { r := rand.New(rand.NewSource(time.Now().Unix())) - helper := testepoching.NewHelper(t) + helper := testhelper.NewHelper(t) msgSrvr := helper.MsgSrvr // enter 1st epoch, in which BBN starts handling validator-related msgs - ctx := helper.GenAndApplyEmptyBlock(r) - wctx := sdk.WrapSDKContext(ctx) + ctx, err := helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) testCases := []struct { name string @@ -64,7 +64,7 @@ func TestMsgWrappedUndelegate(t *testing.T) { } for _, tc := range testCases { wrappedMsg := types.NewMsgWrappedUndelegate(tc.req) - _, err := msgSrvr.WrappedUndelegate(wctx, wrappedMsg) + _, err := msgSrvr.WrappedUndelegate(ctx, wrappedMsg) if tc.expectErr { require.Error(t, err) } else { @@ -75,11 +75,11 @@ func TestMsgWrappedUndelegate(t *testing.T) { func TestMsgWrappedBeginRedelegate(t *testing.T) { r := rand.New(rand.NewSource(time.Now().Unix())) - helper := testepoching.NewHelper(t) + helper := testhelper.NewHelper(t) msgSrvr := helper.MsgSrvr // enter 1st epoch, in which BBN starts handling validator-related msgs - ctx := helper.GenAndApplyEmptyBlock(r) - wctx := sdk.WrapSDKContext(ctx) + ctx, err := helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) testCases := []struct { name string @@ -95,7 +95,38 @@ func TestMsgWrappedBeginRedelegate(t *testing.T) { for _, tc := range testCases { wrappedMsg := types.NewMsgWrappedBeginRedelegate(tc.req) - _, err := msgSrvr.WrappedBeginRedelegate(wctx, wrappedMsg) + _, err := msgSrvr.WrappedBeginRedelegate(ctx, wrappedMsg) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + } +} + +func TestMsgWrappedCancelUnbondingDelegation(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().Unix())) + helper := testhelper.NewHelper(t) + msgSrvr := helper.MsgSrvr + // enter 1st epoch, in which BBN starts handling validator-related msgs + ctx, err := helper.ApplyEmptyBlockWithVoteExtension(r) + require.NoError(t, err) + + testCases := []struct { + name string + req *stakingtypes.MsgCancelUnbondingDelegation + expectErr bool + }{ + { + "empty wrapped msg", + &stakingtypes.MsgCancelUnbondingDelegation{}, + true, + }, + } + for _, tc := range testCases { + wrappedMsg := types.NewMsgWrappedCancelUnbondingDelegation(tc.req) + + _, err := msgSrvr.WrappedCancelUnbondingDelegation(ctx, wrappedMsg) if tc.expectErr { require.Error(t, err) } else { diff --git a/x/epoching/keeper/params.go b/x/epoching/keeper/params.go index 255ea6b27..3f0a67762 100644 --- a/x/epoching/keeper/params.go +++ b/x/epoching/keeper/params.go @@ -1,28 +1,33 @@ package keeper import ( + "context" "github.com/babylonchain/babylon/x/epoching/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) -// GetParams get all parameters as types.Params // SetParams sets the x/epoching module parameters. -func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { if err := p.Validate(); err != nil { return err } - store := ctx.KVStore(k.storeKey) + store := k.storeService.OpenKVStore(ctx) bz := k.cdc.MustMarshal(&p) - store.Set(types.ParamsKey, bz) + err := store.Set(types.ParamsKey, bz) + if err != nil { + return err + } return nil } // GetParams returns the current x/epoching module parameters. -func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ParamsKey) +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } if bz == nil { return p } diff --git a/x/epoching/keeper/staking_functions.go b/x/epoching/keeper/staking_functions.go index 3ff138444..577e7c88e 100644 --- a/x/epoching/keeper/staking_functions.go +++ b/x/epoching/keeper/staking_functions.go @@ -1,11 +1,14 @@ package keeper import ( + "context" + + errorsmod "cosmossdk.io/errors" + cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - errorsmod "cosmossdk.io/errors" "github.com/babylonchain/babylon/x/epoching/types" ) @@ -14,7 +17,8 @@ import ( // The checkpointing module will use this function to verify the `MsgCreateValidator` message // inside a `MsgWrappedCreateValidator` message. // (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.46.10/x/staking/keeper/msg_server.go#L34-L108) -func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCreateValidator) error { +func (k Keeper) CheckMsgCreateValidator(ctx context.Context, msg *stakingtypes.MsgCreateValidator) error { + sdkCtx := sdk.UnwrapSDKContext(ctx) // ensure validator address is correctly encoded valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) if err != nil { @@ -22,7 +26,10 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr } // get parameters of the staking module - sParams := k.stk.GetParams(ctx) + sParams, err := k.stk.GetParams(ctx) + if err != nil { + return err + } // check commission rate if msg.Commission.Rate.LT(sParams.MinCommissionRate) { @@ -30,7 +37,7 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr } // ensure the validator operator was not registered before - if _, found := k.stk.GetValidator(ctx, valAddr); found { + if _, err := k.stk.GetValidator(ctx, valAddr); err == nil { return stakingtypes.ErrValidatorOwnerExists } @@ -41,7 +48,7 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr } // ensure the validator was not registered before - if _, found := k.stk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found { + if _, err := k.stk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); err == nil { return stakingtypes.ErrValidatorPubKeyExists } @@ -58,8 +65,8 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr } // ensure public key type is supported - cp := ctx.ConsensusParams() - if cp != nil && cp.Validator != nil { + cp := sdkCtx.ConsensusParams() + if cp.Validator != nil { pkType := pk.Type() hasKeyType := false for _, keyType := range cp.Validator.PubKeyTypes { @@ -77,7 +84,7 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr } // check validator - validator, err := stakingtypes.NewValidator(valAddr, pk, msg.Description) + validator, err := stakingtypes.NewValidator(valAddr.String(), pk, msg.Description) if err != nil { return err } @@ -85,14 +92,14 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr // check if SetInitialCommission fails or not commission := stakingtypes.NewCommissionWithTime( msg.Commission.Rate, msg.Commission.MaxRate, - msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, + msg.Commission.MaxChangeRate, sdkCtx.HeaderInfo().Time, ) if _, err := validator.SetInitialCommission(commission); err != nil { return err } - // sanity check on delegator address - delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + // sanity check on delegator address -- delegator is the same as validator + delegatorAddr := sdk.AccAddress(valAddr) if err != nil { return err } @@ -104,3 +111,7 @@ func (k Keeper) CheckMsgCreateValidator(ctx sdk.Context, msg *stakingtypes.MsgCr return nil } + +func (k Keeper) GetPubKeyByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (cmtprotocrypto.PublicKey, error) { + return k.stk.GetPubKeyByConsAddr(ctx, consAddr) +} diff --git a/x/epoching/module.go b/x/epoching/module.go index f8215345c..f21bdcdcb 100644 --- a/x/epoching/module.go +++ b/x/epoching/module.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" + "cosmossdk.io/core/appmodule" + "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" @@ -22,9 +24,9 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} ) // ---------------------------------------------------------------------------- @@ -64,7 +66,7 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { } // ValidateGenesis performs genesis state validation for the capability module. -func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { var genState types.GenesisState if err := cdc.UnmarshalJSON(bz, &genState); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) @@ -94,6 +96,7 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { // ---------------------------------------------------------------------------- // AppModule // ---------------------------------------------------------------------------- +var _ appmodule.AppModule = AppModule{} // AppModule implements the AppModule interface for the capability module. type AppModule struct { @@ -141,14 +144,12 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the capability module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. @@ -160,10 +161,18 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // ConsensusVersion implements ConsensusVersion. func (AppModule) ConsensusVersion() uint64 { return 1 } -func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper, req) +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) } -func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { return EndBlocker(ctx, am.keeper) } + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker +} diff --git a/x/epoching/module_simulation.go b/x/epoching/module_simulation.go deleted file mode 100644 index f5591fede..000000000 --- a/x/epoching/module_simulation.go +++ /dev/null @@ -1,42 +0,0 @@ -package epoching - -import ( - simappparams "github.com/babylonchain/babylon/app/params" - epochingsimulation "github.com/babylonchain/babylon/x/epoching/simulation" - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = epochingsimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - epochingsimulation.RandomizedGenState(simState) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[types.StoreKey] = epochingsimulation.NewDecodeStore(am.cdc) -} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - return epochingsimulation.WeightedOperations( - simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.keeper, - ) -} diff --git a/x/epoching/simulation/decoder.go b/x/epoching/simulation/decoder.go deleted file mode 100644 index 9c3bccc45..000000000 --- a/x/epoching/simulation/decoder.go +++ /dev/null @@ -1,63 +0,0 @@ -package simulation - -import ( - "bytes" - "fmt" - - errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/kv" -) - -// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's -// Value to the corresponding epoching type. -func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { - return func(kvA, kvB kv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.EpochNumberKey), - bytes.Equal(kvA.Key[:1], types.QueueLengthKey): - return fmt.Sprintf("%v\n%v", sdk.BigEndianToUint64(kvA.Value), sdk.BigEndianToUint64(kvB.Value)) - - case bytes.Equal(kvA.Key[:1], types.MsgQueueKey): - var qmA, qmB sdk.Msg - err := cdc.UnmarshalInterface(kvA.Value, &qmA) - if err != nil { - panic(err) - } - err = cdc.UnmarshalInterface(kvB.Value, &qmB) - if err != nil { - panic(err) - } - return fmt.Sprintf("%v\n%v", qmA.(*types.QueuedMessage).MsgId, qmB.(*types.QueuedMessage).MsgId) - - case bytes.Equal(kvA.Key[:1], types.ValidatorSetKey), - bytes.Equal(kvA.Key[:1], types.SlashedValidatorSetKey): - valSetA, err := types.NewValidatorSetFromBytes(kvA.Value) - if err != nil { - panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) - } - valSetB, err := types.NewValidatorSetFromBytes(kvB.Value) - if err != nil { - panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) - } - return fmt.Sprintf("%v\n%v", valSetA, valSetB) - - case bytes.Equal(kvA.Key[:1], types.VotingPowerKey), - bytes.Equal(kvA.Key[:1], types.SlashedVotingPowerKey): - var powerA, powerB math.Int - if err := powerA.Unmarshal(kvA.Value); err != nil { - panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) - } - if err := powerB.Unmarshal(kvA.Value); err != nil { - panic(errorsmod.Wrap(types.ErrUnmarshal, err.Error())) - } - return fmt.Sprintf("%v\n%v", powerA, powerB) - - default: - panic(fmt.Sprintf("invalid epoching key prefix %X", kvA.Key[:1])) - } - } -} diff --git a/x/epoching/simulation/decoder_test.go b/x/epoching/simulation/decoder_test.go deleted file mode 100644 index 79f173215..000000000 --- a/x/epoching/simulation/decoder_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package simulation_test - -import ( - "fmt" - "testing" - - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/kv" - - "github.com/babylonchain/babylon/app" - "github.com/babylonchain/babylon/x/epoching/simulation" - "github.com/babylonchain/babylon/x/epoching/types" -) - -// nolint:deadcode,unused,varcheck -var ( - delPk1 = ed25519.GenPrivKey().PubKey() - delAddr1 = sdk.AccAddress(delPk1.Address()) - valAddr1 = sdk.ValAddress(delPk1.Address()) - consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) - oneBytes, _ = sdk.NewInt(1).Marshal() -) - -func TestDecodeStore(t *testing.T) { - cdc := app.GetEncodingConfig().Marshaler - dec := simulation.NewDecodeStore(cdc) - - epochNumber := uint64(123) - queuedMsg := types.QueuedMessage{ - TxId: sdk.Uint64ToBigEndian(333), - MsgId: sdk.Uint64ToBigEndian(444), - Msg: &types.QueuedMessage_MsgDelegate{MsgDelegate: &stakingtypes.MsgDelegate{}}, - } - valSet := types.ValidatorSet{ - types.Validator{ - Addr: valAddr1, - Power: 1, - }, - } - - marshaledQueueMsg, err := cdc.MarshalInterface(&queuedMsg) - require.NoError(t, err) - kvPairs := kv.Pairs{ - Pairs: []kv.Pair{ - {Key: types.EpochNumberKey, Value: sdk.Uint64ToBigEndian(epochNumber)}, - {Key: types.MsgQueueKey, Value: marshaledQueueMsg}, - {Key: types.ValidatorSetKey, Value: valSet.MustMarshal()}, - {Key: types.SlashedValidatorSetKey, Value: valSet.MustMarshal()}, - {Key: types.VotingPowerKey, Value: oneBytes}, - {Key: types.SlashedVotingPowerKey, Value: oneBytes}, - {Key: []byte{0x99}, Value: []byte{0x99}}, // This test should panic - }, - } - - tests := []struct { - name string - expectedLog string - }{ - {"EpochNumber", fmt.Sprintf("%v\n%v", epochNumber, epochNumber)}, - {"QueuedMsg", fmt.Sprintf("%v\n%v", queuedMsg.MsgId, queuedMsg.MsgId)}, - {"ValidatorSet", fmt.Sprintf("%v\n%v", valSet, valSet)}, - {"SlashedValidatorSet", fmt.Sprintf("%v\n%v", valSet, valSet)}, - {"VotingPower", fmt.Sprintf("%v\n%v", sdk.NewInt(1), sdk.NewInt(1))}, - {"SlashedVotingPower", fmt.Sprintf("%v\n%v", sdk.NewInt(1), sdk.NewInt(1))}, - {"other", ""}, - } - for i, tt := range tests { - i, tt := i, tt - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) - } - }) - } -} diff --git a/x/epoching/simulation/genesis.go b/x/epoching/simulation/genesis.go deleted file mode 100644 index 05bbd0515..000000000 --- a/x/epoching/simulation/genesis.go +++ /dev/null @@ -1,40 +0,0 @@ -package simulation - -// DONTCOVER - -import ( - "encoding/json" - "fmt" - "math/rand" - - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/types/module" -) - -// Simulation parameter constants -const ( - EpochIntervalKey = "epoch_interval" -) - -// genEpochInterval returns randomized EpochInterval -func genEpochInterval(r *rand.Rand) uint64 { - return uint64(r.Intn(10) + 2) -} - -// RandomizedGenState generates a random GenesisState for staking -func RandomizedGenState(simState *module.SimulationState) { - var epochInterval uint64 - simState.AppParams.GetOrGenerate( - simState.Cdc, EpochIntervalKey, &epochInterval, simState.Rand, - func(r *rand.Rand) { epochInterval = genEpochInterval(r) }, - ) - params := types.NewParams(epochInterval) - epochingGenesis := types.NewGenesis(params) - - bz, err := json.MarshalIndent(&epochingGenesis.Params, "", " ") - if err != nil { - panic(err) - } - fmt.Printf("Selected randomly generated epoching parameters:\n%s\n", bz) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(epochingGenesis) -} diff --git a/x/epoching/simulation/genesis_test.go b/x/epoching/simulation/genesis_test.go deleted file mode 100644 index feda296ef..000000000 --- a/x/epoching/simulation/genesis_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package simulation_test - -import ( - "encoding/json" - "math/rand" - "testing" - - sdkmath "cosmossdk.io/math" - - "github.com/stretchr/testify/require" - - "github.com/babylonchain/babylon/x/epoching/simulation" - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState given a fixed seed. -// Abonormal scenarios are not tested here. -func TestRandomizedGenState(t *testing.T) { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cryptocodec.RegisterInterfaces(interfaceRegistry) - cdc := codec.NewProtoCodec(interfaceRegistry) - - s := rand.NewSource(1) - r := rand.New(s) - - simState := module.SimulationState{ - AppParams: make(simtypes.AppParams), - Cdc: cdc, - Rand: r, - NumBonded: 3, - Accounts: simtypes.RandomAccounts(r, 3), - InitialStake: sdkmath.NewInt(1000), - GenState: make(map[string]json.RawMessage), - } - - simulation.RandomizedGenState(&simState) - - var epochingGenesis types.GenesisState - simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &epochingGenesis) - - require.Equal(t, uint64(0x2), epochingGenesis.Params.EpochInterval) -} - -// TestRandomizedGenState1 tests abnormal scenarios of applying RandomizedGenState. -func TestRandomizedGenState1(t *testing.T) { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(interfaceRegistry) - - s := rand.NewSource(1) - r := rand.New(s) - // all these tests will panic - tests := []struct { - simState module.SimulationState - panicMsg string - }{ - { // panic => reason: incomplete initialization of the simState - module.SimulationState{}, "invalid memory address or nil pointer dereference"}, - { // panic => reason: incomplete initialization of the simState - module.SimulationState{ - AppParams: make(simtypes.AppParams), - Cdc: cdc, - Rand: r, - }, "invalid memory address or nil pointer dereference"}, - } - - for _, tt := range tests { - require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) - } -} diff --git a/x/epoching/simulation/operations.go b/x/epoching/simulation/operations.go deleted file mode 100644 index e338bde0a..000000000 --- a/x/epoching/simulation/operations.go +++ /dev/null @@ -1,329 +0,0 @@ -package simulation - -import ( - "fmt" - "math/rand" - - "github.com/babylonchain/babylon/x/epoching/keeper" - "github.com/babylonchain/babylon/x/epoching/types" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" - simappparams "github.com/cosmos/cosmos-sdk/x/staking/simulation" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// Simulation operation weights constants -const ( - OpWeightMsgWrappedDelegate = "op_weight_msg_wrapped_delegate" - OpWeightMsgWrappedUndelegate = "op_weight_msg_wrapped_undelegate" - OpWeightMsgWrappedBeginRedelegate = "op_weight_msg_wrapped_begin_redelegate" -) - -// WeightedOperations returns all the operations from the module with their respective weights -func WeightedOperations( - appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, bk types.BankKeeper, stk types.StakingKeeper, k keeper.Keeper, -) simulation.WeightedOperations { - var ( - weightMsgWrappedDelegate int - weightMsgWrappedUndelegate int - weightMsgWrappedBeginRedelegate int - ) - - appParams.GetOrGenerate(cdc, OpWeightMsgWrappedDelegate, &weightMsgWrappedDelegate, nil, - func(_ *rand.Rand) { - weightMsgWrappedDelegate = simappparams.DefaultWeightMsgDelegate // TODO: use our own (and randomised) weight rather than those from the unwrapped msgs - }, - ) - - appParams.GetOrGenerate(cdc, OpWeightMsgWrappedUndelegate, &weightMsgWrappedUndelegate, nil, - func(_ *rand.Rand) { - weightMsgWrappedUndelegate = simappparams.DefaultWeightMsgUndelegate // TODO: use our own (and randomised) weight rather than those from the unwrapped msgs - }, - ) - - appParams.GetOrGenerate(cdc, OpWeightMsgWrappedBeginRedelegate, &weightMsgWrappedBeginRedelegate, nil, - func(_ *rand.Rand) { - weightMsgWrappedBeginRedelegate = simappparams.DefaultWeightMsgBeginRedelegate // TODO: use our own (and randomised) weight rather than those from the unwrapped msgs - }, - ) - - return simulation.WeightedOperations{ - simulation.NewWeightedOperation( - weightMsgWrappedDelegate, - SimulateMsgWrappedDelegate(ak, bk, stk, k), - ), - simulation.NewWeightedOperation( - weightMsgWrappedUndelegate, - SimulateMsgWrappedUndelegate(ak, bk, stk, k), - ), - simulation.NewWeightedOperation( - weightMsgWrappedBeginRedelegate, - SimulateMsgWrappedBeginRedelegate(ak, bk, stk, k), - ), - } -} - -// SimulateMsgDelegate generates a MsgDelegate with random values -func SimulateMsgWrappedDelegate(ak types.AccountKeeper, bk types.BankKeeper, stk types.StakingKeeper, k keeper.Keeper) simtypes.Operation { - return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, - ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - epoch := k.GetEpoch(ctx) - valSet := k.GetValidatorSet(ctx, epoch.EpochNumber) - if len(valSet) == 0 { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "number of validators in this epoch equal zero"), nil, nil - } - - // pick a random validator - i := r.Intn(len(valSet)) - val, ok := stk.GetValidator(ctx, valSet[i].Addr) - if !ok { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "unable to pick a validator"), nil, nil - } - if val.InvalidExRate() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "validator's invalid exchange rate"), nil, nil - } - - // pick a random bondAmt - simAccount, _ := simtypes.RandomAcc(r, accs) - denom := stk.GetParams(ctx).BondDenom - amount := bk.GetBalance(ctx, simAccount.Address, denom).Amount - if !amount.IsPositive() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "balance is negative"), nil, nil - } - amount, err := simtypes.RandPositiveInt(r, amount) - if err != nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "unable to generate positive amount"), nil, err - } - bondAmt := sdk.NewCoin(denom, amount) - - // pick a random fee rate - var fees sdk.Coins - account := ak.GetAccount(ctx, simAccount.Address) - spendable := bk.SpendableCoins(ctx, account.GetAddress()) - coins, hasNeg := spendable.SafeSub(sdk.Coins{bondAmt}...) - if !hasNeg { - fees, err = simtypes.RandomFees(r, ctx, coins) - if err != nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedDelegate, "unable to generate fees"), nil, err - } - } - - msg := stakingtypes.NewMsgDelegate(simAccount.Address, val.GetOperator(), bondAmt) - wmsg := types.NewMsgWrappedDelegate(msg) - txCtx := simulation.OperationInput{ - App: app, - TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: wmsg, - MsgType: wmsg.Type(), - Context: ctx, - SimAccount: simAccount, - AccountKeeper: ak, - ModuleName: types.ModuleName, - } - - return simulation.GenAndDeliverTx(txCtx, fees) - } -} - -// SimulateMsgUndelegate generates a MsgUndelegate with random values -func SimulateMsgWrappedUndelegate(ak types.AccountKeeper, bk types.BankKeeper, stk types.StakingKeeper, k keeper.Keeper) simtypes.Operation { - return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, - ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - epoch := k.GetEpoch(ctx) - valSet := k.GetValidatorSet(ctx, epoch.EpochNumber) - if len(valSet) == 0 { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "number of validators in this epoch equal zero"), nil, nil - } - - // pick a random validator - i := r.Intn(len(valSet)) - val, ok := stk.GetValidator(ctx, valSet[i].Addr) - if !ok { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "unable to pick a validator"), nil, nil - } - if val.InvalidExRate() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "validator's invalid exchange rate"), nil, nil - } - - // pick a random delegator from validator - valAddr := val.GetOperator() - delegations := stk.GetValidatorDelegations(ctx, val.GetOperator()) - if delegations == nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "keeper does not have any delegation entries"), nil, nil - } - delegation := delegations[r.Intn(len(delegations))] - delAddr := delegation.GetDelegatorAddr() - - if stk.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "keeper reaches max unbonding delegation entries"), nil, nil - } - - // pick a random unbondAmt - totalBond := val.TokensFromShares(delegation.GetShares()).TruncateInt() - if !totalBond.IsPositive() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "total bond is negative"), nil, nil - } - unbondAmt, err := simtypes.RandPositiveInt(r, totalBond) - if err != nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "invalid unbond amount"), nil, err - } - if unbondAmt.IsZero() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedUndelegate, "unbond amount is zero"), nil, nil - } - - msg := stakingtypes.NewMsgUndelegate( - delAddr, valAddr, sdk.NewCoin(stk.BondDenom(ctx), unbondAmt), - ) - wmsg := types.NewMsgWrappedUndelegate(msg) - - // need to retrieve the simulation account associated with delegation to retrieve PrivKey - var simAccount simtypes.Account - - for _, simAcc := range accs { - if simAcc.Address.Equals(delAddr) { - simAccount = simAcc - break - } - } - // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error - if simAccount.PrivKey == nil { - return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) - } - - account := ak.GetAccount(ctx, delAddr) - spendable := bk.SpendableCoins(ctx, account.GetAddress()) - - txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: wmsg, - MsgType: wmsg.Type(), - Context: ctx, - SimAccount: simAccount, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, - CoinsSpentInMsg: spendable, - } - - return simulation.GenAndDeliverTxWithRandFees(txCtx) - } -} - -// SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values -func SimulateMsgWrappedBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, stk types.StakingKeeper, k keeper.Keeper) simtypes.Operation { - return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, - ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - epoch := k.GetEpoch(ctx) - valSet := k.GetValidatorSet(ctx, epoch.EpochNumber) - if len(valSet) == 0 { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "number of validators in this epoch equal zero"), nil, nil - } - - // pick a random source validator - i := r.Intn(len(valSet)) - srcVal, ok := stk.GetValidator(ctx, valSet[i].Addr) - if !ok { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "unable to pick a validator"), nil, nil - } - - srcAddr := srcVal.GetOperator() - delegations := stk.GetValidatorDelegations(ctx, srcAddr) - if delegations == nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "keeper does have any delegation entries"), nil, nil - } - - // pick a random delegator from src validator - delegation := delegations[r.Intn(len(delegations))] - delAddr := delegation.GetDelegatorAddr() - - if stk.HasReceivingRedelegation(ctx, delAddr, srcAddr) { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "receiving redelegation is not allowed"), nil, nil // skip - } - - // pick a random destination validator - i = r.Intn(len(valSet)) - destVal, ok := stk.GetValidator(ctx, valSet[i].Addr) - if !ok { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "unable to pick a validator"), nil, nil - } - destAddr := destVal.GetOperator() - if srcAddr.Equals(destAddr) || destVal.InvalidExRate() || stk.HasMaxRedelegationEntries(ctx, delAddr, srcAddr, destAddr) { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "checks failed"), nil, nil - } - - // pick a random redAmt - totalBond := srcVal.TokensFromShares(delegation.GetShares()).TruncateInt() - if !totalBond.IsPositive() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "total bond is negative"), nil, nil - } - redAmt, err := simtypes.RandPositiveInt(r, totalBond) - if err != nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "unable to generate positive amount"), nil, err - } - if redAmt.IsZero() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "amount is zero"), nil, nil - } - - // check if the shares truncate to zero - shares, err := srcVal.SharesFromTokens(redAmt) - if err != nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "invalid shares"), nil, err - } - - if srcVal.TokensFromShares(shares).TruncateInt().IsZero() { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "shares truncate to zero"), nil, nil // skip - } - - // need to retrieve the simulation account associated with delegation to retrieve PrivKey - var simAccount simtypes.Account - - for _, simAcc := range accs { - if simAcc.Address.Equals(delAddr) { - simAccount = simAcc - break - } - } - - // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error - if simAccount.PrivKey == nil { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWrappedBeginRedelegate, "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) - } - - account := ak.GetAccount(ctx, delAddr) - spendable := bk.SpendableCoins(ctx, account.GetAddress()) - - msg := stakingtypes.NewMsgBeginRedelegate( - delAddr, srcAddr, destAddr, - sdk.NewCoin(stk.BondDenom(ctx), redAmt), - ) - wmsg := types.NewMsgWrappedBeginRedelegate(msg) - - txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: wmsg, - MsgType: wmsg.Type(), - Context: ctx, - SimAccount: simAccount, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, - CoinsSpentInMsg: spendable, - } - - return simulation.GenAndDeliverTxWithRandFees(txCtx) - } -} diff --git a/x/epoching/simulation/operations_test.go b/x/epoching/simulation/operations_test.go deleted file mode 100644 index 5c6452f9a..000000000 --- a/x/epoching/simulation/operations_test.go +++ /dev/null @@ -1 +0,0 @@ -package simulation_test diff --git a/x/epoching/simulation/simap.go b/x/epoching/simulation/simap.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/epoching/simulation/simap.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/epoching/testepoching/README.md b/x/epoching/testepoching/README.md deleted file mode 100644 index f5ff163cc..000000000 --- a/x/epoching/testepoching/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# testepoching - -The code under this directory provides infrastructure for testing the epoching module, such as mocking staking module messages, parsing Tenderming/Cosmos validators, etc.. The code is adapted from https://github.com/cosmos/cosmos-sdk/tree/v0.45.5/x/staking/teststaking \ No newline at end of file diff --git a/x/epoching/testepoching/helper.go b/x/epoching/testepoching/helper.go deleted file mode 100644 index 44ac8672f..000000000 --- a/x/epoching/testepoching/helper.go +++ /dev/null @@ -1,238 +0,0 @@ -package testepoching - -import ( - "math/rand" - "testing" - - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/testutil/datagen" - - "cosmossdk.io/math" - proto "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/require" - - appparams "github.com/babylonchain/babylon/app/params" - - abci "github.com/cometbft/cometbft/abci/types" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/babylonchain/babylon/app" - "github.com/babylonchain/babylon/x/epoching/keeper" - "github.com/babylonchain/babylon/x/epoching/types" -) - -type ValidatorInfo struct { - BlsKey bls12381.PrivateKey - Address sdk.ValAddress -} - -// Helper is a structure which wraps the entire app and exposes functionalities for testing the epoching module -type Helper struct { - t *testing.T - - Ctx sdk.Context - App *app.BabylonApp - EpochingKeeper *keeper.Keeper - MsgSrvr types.MsgServer - QueryClient types.QueryClient - StakingKeeper *stakingkeeper.Keeper - - GenAccs []authtypes.GenesisAccount - ValBlsPrivKeys []ValidatorInfo -} - -// NewHelper creates the helper for testing the epoching module -func NewHelper(t *testing.T) *Helper { - app := app.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - epochingKeeper := app.EpochingKeeper - - // add BLS pubkey to the genesis validator - valSet := epochingKeeper.GetValidatorSet(ctx, 0) - require.Len(t, valSet, 1) - genesisVal := valSet[0] - blsPrivKey := bls12381.GenPrivKey() - genesisBLSPubkey := blsPrivKey.PubKey() - err := app.CheckpointingKeeper.CreateRegistration(ctx, genesisBLSPubkey, genesisVal.Addr) - require.NoError(t, err) - - querier := keeper.Querier{Keeper: epochingKeeper} - queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, querier) - queryClient := types.NewQueryClient(queryHelper) - msgSrvr := keeper.NewMsgServerImpl(epochingKeeper) - - return &Helper{ - t, - ctx, - app, - &epochingKeeper, - msgSrvr, - queryClient, - app.StakingKeeper, - nil, - []ValidatorInfo{ValidatorInfo{ - blsPrivKey, - genesisVal.Addr, - }}, - } -} - -// NewHelperWithValSet is same as NewHelper, except that it creates a set of validators -func NewHelperWithValSet(t *testing.T) *Helper { - // generate the validator set with 10 validators - tmValSet, err := GenTmValidatorSet(10) - require.NoError(t, err) - - // generate the genesis account - senderPrivKey := secp256k1.GenPrivKey() - acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) - // ensure the genesis account has a sufficient amount of tokens - balance := banktypes.Balance{ - Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.DefaultPowerReduction.MulRaw(10000000))), - } - GenAccs := []authtypes.GenesisAccount{acc} - - // setup the app and ctx - app := app.SetupWithGenesisValSet(t, tmValSet, GenAccs, balance) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - // get necessary subsets of the app/keeper - epochingKeeper := app.EpochingKeeper - valInfos := []ValidatorInfo{} - // add BLS pubkey to the genesis validator - valSet := epochingKeeper.GetValidatorSet(ctx, 0) - for _, val := range valSet { - blsPrivKey := bls12381.GenPrivKey() - valInfos = append(valInfos, ValidatorInfo{blsPrivKey, val.Addr}) - blsPubkey := blsPrivKey.PubKey() - err = app.CheckpointingKeeper.CreateRegistration(ctx, blsPubkey, val.Addr) - require.NoError(t, err) - } - querier := keeper.Querier{Keeper: epochingKeeper} - queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, querier) - queryClient := types.NewQueryClient(queryHelper) - msgSrvr := keeper.NewMsgServerImpl(epochingKeeper) - - return &Helper{t, ctx, app, &epochingKeeper, msgSrvr, queryClient, app.StakingKeeper, GenAccs, valInfos} -} - -// GenAndApplyEmptyBlock generates a new empty block and appends it to the current blockchain -func (h *Helper) GenAndApplyEmptyBlock(r *rand.Rand) sdk.Context { - newHeight := h.App.LastBlockHeight() + 1 - valSet := h.StakingKeeper.GetLastValidators(h.Ctx) - valhash := CalculateValHash(valSet) - newHeader := tmproto.Header{ - Height: newHeight, - ValidatorsHash: valhash, - NextValidatorsHash: valhash, - AppHash: datagen.GenRandomByteArray(r, 32), - LastCommitHash: datagen.GenRandomLastCommitHash(r), - } - - h.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader}) - h.App.EndBlock(abci.RequestEndBlock{}) - h.App.Commit() - - h.Ctx = h.Ctx.WithBlockHeader(newHeader) - return h.Ctx -} - -func (h *Helper) BeginBlock(r *rand.Rand) sdk.Context { - newHeight := h.App.LastBlockHeight() + 1 - valSet := h.StakingKeeper.GetLastValidators(h.Ctx) - valhash := CalculateValHash(valSet) - newHeader := tmproto.Header{ - Height: newHeight, - AppHash: datagen.GenRandomByteArray(r, 32), - ValidatorsHash: valhash, - NextValidatorsHash: valhash, - } - - h.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader}) - h.Ctx = h.Ctx.WithBlockHeader(newHeader) - return h.Ctx -} - -func (h *Helper) EndBlock() sdk.Context { - h.App.EndBlock(abci.RequestEndBlock{}) - h.App.Commit() - return h.Ctx -} - -// WrappedDelegate calls handler to delegate stake for a validator -func (h *Helper) WrappedDelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount math.Int) *sdk.Result { - coin := sdk.NewCoin(appparams.DefaultBondDenom, amount) - msg := stakingtypes.NewMsgDelegate(delegator, val, coin) - wmsg := types.NewMsgWrappedDelegate(msg) - return h.Handle(func(ctx sdk.Context) (proto.Message, error) { - return h.MsgSrvr.WrappedDelegate(ctx, wmsg) - }) -} - -// WrappedDelegateWithPower calls handler to delegate stake for a validator -func (h *Helper) WrappedDelegateWithPower(delegator sdk.AccAddress, val sdk.ValAddress, power int64) *sdk.Result { - coin := sdk.NewCoin(appparams.DefaultBondDenom, h.StakingKeeper.TokensFromConsensusPower(h.Ctx, power)) - msg := stakingtypes.NewMsgDelegate(delegator, val, coin) - wmsg := types.NewMsgWrappedDelegate(msg) - return h.Handle(func(ctx sdk.Context) (proto.Message, error) { - return h.MsgSrvr.WrappedDelegate(ctx, wmsg) - }) -} - -// WrappedUndelegate calls handler to unbound some stake from a validator. -func (h *Helper) WrappedUndelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount math.Int) *sdk.Result { - unbondAmt := sdk.NewCoin(appparams.DefaultBondDenom, amount) - msg := stakingtypes.NewMsgUndelegate(delegator, val, unbondAmt) - wmsg := types.NewMsgWrappedUndelegate(msg) - return h.Handle(func(ctx sdk.Context) (proto.Message, error) { - return h.MsgSrvr.WrappedUndelegate(ctx, wmsg) - }) -} - -// WrappedBeginRedelegate calls handler to redelegate some stake from a validator to another -func (h *Helper) WrappedBeginRedelegate(delegator sdk.AccAddress, srcVal sdk.ValAddress, dstVal sdk.ValAddress, amount math.Int) *sdk.Result { - unbondAmt := sdk.NewCoin(appparams.DefaultBondDenom, amount) - msg := stakingtypes.NewMsgBeginRedelegate(delegator, srcVal, dstVal, unbondAmt) - wmsg := types.NewMsgWrappedBeginRedelegate(msg) - return h.Handle(func(ctx sdk.Context) (proto.Message, error) { - return h.MsgSrvr.WrappedBeginRedelegate(ctx, wmsg) - }) -} - -// Handle executes an action function with the Helper's context, wraps the result into an SDK service result, and performs two assertions before returning it -func (h *Helper) Handle(action func(sdk.Context) (proto.Message, error)) *sdk.Result { - res, err := action(h.Ctx) - r, _ := sdk.WrapServiceResult(h.Ctx, res, err) - require.NotNil(h.t, r) - require.NoError(h.t, err) - return r -} - -// CheckValidator asserts that a validor exists and has a given status (if status!="") -// and if has a right jailed flag. -func (h *Helper) CheckValidator(addr sdk.ValAddress, status stakingtypes.BondStatus, jailed bool) stakingtypes.Validator { - v, ok := h.StakingKeeper.GetValidator(h.Ctx, addr) - require.True(h.t, ok) - require.Equal(h.t, jailed, v.Jailed, "wrong Jalied status") - if status >= 0 { - require.Equal(h.t, status, v.Status) - } - return v -} - -// CheckDelegator asserts that a delegator exists -func (h *Helper) CheckDelegator(delegator sdk.AccAddress, val sdk.ValAddress, found bool) { - _, ok := h.StakingKeeper.GetDelegation(h.Ctx, delegator, val) - require.Equal(h.t, ok, found) -} diff --git a/x/epoching/testepoching/tm.go b/x/epoching/testepoching/tm.go deleted file mode 100644 index c7cc83b08..000000000 --- a/x/epoching/testepoching/tm.go +++ /dev/null @@ -1,59 +0,0 @@ -package testepoching - -import ( - "cosmossdk.io/math" - "github.com/babylonchain/babylon/testutil/datagen" - tmcrypto "github.com/cometbft/cometbft/crypto" - tmtypes "github.com/cometbft/cometbft/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// GetTmConsPubKey gets the validator's public key as a tmcrypto.PubKey. -func GetTmConsPubKey(v stakingtypes.Validator) (tmcrypto.PubKey, error) { - pk, err := v.ConsPubKey() - if err != nil { - return nil, err - } - - return cryptocodec.ToTmPubKeyInterface(pk) -} - -// ToTmValidator casts an SDK validator to a tendermint type Validator. -func ToTmValidator(v stakingtypes.Validator, r math.Int) (*tmtypes.Validator, error) { - tmPk, err := GetTmConsPubKey(v) - if err != nil { - return nil, err - } - - return tmtypes.NewValidator(tmPk, v.ConsensusPower(r)), nil -} - -// ToTmValidators casts all validators to the corresponding tendermint type. -func ToTmValidators(v stakingtypes.Validators, r math.Int) ([]*tmtypes.Validator, error) { - validators := make([]*tmtypes.Validator, len(v)) - var err error - for i, val := range v { - validators[i], err = ToTmValidator(val, r) - if err != nil { - return nil, err - } - } - - return validators, nil -} - -// GenTmValidatorSet generates a set with `numVals` Tendermint validators -func GenTmValidatorSet(numVals int) (*tmtypes.ValidatorSet, error) { - vals := []*tmtypes.Validator{} - for i := 0; i < numVals; i++ { - privVal := datagen.NewPV() - pubKey, err := privVal.GetPubKey() - if err != nil { - return nil, err - } - val := tmtypes.NewValidator(pubKey, 1) - vals = append(vals, val) - } - return tmtypes.NewValidatorSet(vals), nil -} diff --git a/x/epoching/testepoching/utils.go b/x/epoching/testepoching/utils.go deleted file mode 100644 index 087dab2de..000000000 --- a/x/epoching/testepoching/utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package testepoching - -func Min(a, b uint64) uint64 { - if a < b { - return a - } - return b -} diff --git a/x/epoching/testepoching/validator.go b/x/epoching/testepoching/validator.go deleted file mode 100644 index 30400f002..000000000 --- a/x/epoching/testepoching/validator.go +++ /dev/null @@ -1,35 +0,0 @@ -package testepoching - -import ( - "testing" - - "github.com/cometbft/cometbft/crypto/merkle" - "github.com/stretchr/testify/require" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// NewValidator is a testing helper method to create validators in tests -func NewValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey) stakingtypes.Validator { - v, err := stakingtypes.NewValidator(operator, pubKey, stakingtypes.Description{}) - require.NoError(t, err) - return v -} - -// calculate validator hash and new header -// (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.45.5/simapp/test_helpers.go#L156-L163) -func CalculateValHash(valSet []stakingtypes.Validator) []byte { - bzs := make([][]byte, len(valSet)) - for i, val := range valSet { - consAddr, _ := val.GetConsAddr() - bzs[i] = consAddr - } - return merkle.HashFromByteSlices(bzs) -} - -// ZeroCommission constructs a commission rates with all zeros. -func ZeroCommission() stakingtypes.CommissionRates { - return stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) -} diff --git a/x/epoching/types/codec.go b/x/epoching/types/codec.go index cb32c4dae..ddaea8e65 100644 --- a/x/epoching/types/codec.go +++ b/x/epoching/types/codec.go @@ -20,21 +20,9 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { registry.RegisterImplementations( (*sdk.Msg)(nil), &MsgWrappedDelegate{}, - ) - registry.RegisterImplementations( - (*sdk.Msg)(nil), &MsgWrappedUndelegate{}, - ) - registry.RegisterImplementations( - (*sdk.Msg)(nil), &MsgWrappedBeginRedelegate{}, - ) - registry.RegisterImplementations( - (*sdk.Msg)(nil), &QueuedMessage{}, - ) - registry.RegisterImplementations( - (*sdk.Msg)(nil), &MsgUpdateParams{}, ) diff --git a/x/epoching/types/epoching.go b/x/epoching/types/epoching.go index bed9745dc..295fe7671 100644 --- a/x/epoching/types/epoching.go +++ b/x/epoching/types/epoching.go @@ -1,6 +1,7 @@ package types import ( + "context" "time" errorsmod "cosmossdk.io/errors" @@ -9,7 +10,6 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cometbft/cometbft/crypto/tmhash" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -17,12 +17,12 @@ import ( // The relationship between block and epoch is as follows, assuming epoch interval of 5: // 0 | 1 2 3 4 5 | 6 7 8 9 10 | // 0 | 1 | 2 | -func NewEpoch(epochNumber uint64, epochInterval uint64, firstBlockHeight uint64, lastBlockHeader *tmproto.Header) Epoch { +func NewEpoch(epochNumber uint64, epochInterval uint64, firstBlockHeight uint64, lastBlockTime *time.Time) Epoch { return Epoch{ EpochNumber: epochNumber, CurrentEpochInterval: epochInterval, FirstBlockHeight: firstBlockHeight, - LastBlockHeader: lastBlockHeader, + LastBlockTime: lastBlockTime, // NOTE: SealerHeader will be set in the next epoch } } @@ -34,6 +34,10 @@ func (e Epoch) GetLastBlockHeight() uint64 { return e.FirstBlockHeight + e.CurrentEpochInterval - 1 } +func (e Epoch) GetSealerBlockHeight() uint64 { + return e.GetLastBlockHeight() + 1 +} + func (e Epoch) GetSecondBlockHeight() uint64 { if e.EpochNumber == 0 { panic("should not be called when epoch number is zero") @@ -41,19 +45,30 @@ func (e Epoch) GetSecondBlockHeight() uint64 { return e.FirstBlockHeight + 1 } -func (e Epoch) IsLastBlock(ctx sdk.Context) bool { - return e.GetLastBlockHeight() == uint64(ctx.BlockHeight()) +func (e Epoch) IsLastBlock(ctx context.Context) bool { + return e.GetLastBlockHeight() == uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) } -func (e Epoch) IsFirstBlock(ctx sdk.Context) bool { - return e.FirstBlockHeight == uint64(ctx.BlockHeight()) +func (e Epoch) IsLastBlockByHeight(height int64) bool { + return e.GetLastBlockHeight() == uint64(height) +} + +func (e Epoch) IsFirstBlock(ctx context.Context) bool { + return e.FirstBlockHeight == uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) +} + +func (e Epoch) IsSecondBlock(ctx context.Context) bool { + if e.EpochNumber == 0 { + return false + } + return e.GetSecondBlockHeight() == uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) } -func (e Epoch) IsSecondBlock(ctx sdk.Context) bool { +func (e Epoch) IsVoteExtensionProposal(ctx context.Context) bool { if e.EpochNumber == 0 { return false } - return e.GetSecondBlockHeight() == uint64(ctx.BlockHeight()) + return e.IsFirstBlockOfNextEpoch(ctx) } // IsFirstBlockOfNextEpoch checks whether the current block is the first block of @@ -61,18 +76,19 @@ func (e Epoch) IsSecondBlock(ctx sdk.Context) bool { // CONTRACT: IsFirstBlockOfNextEpoch can only be called by the epoching module // once upon the first block of a new epoch // other modules should use IsFirstBlock instead. -func (e Epoch) IsFirstBlockOfNextEpoch(ctx sdk.Context) bool { +func (e Epoch) IsFirstBlockOfNextEpoch(ctx context.Context) bool { + sdkCtx := sdk.UnwrapSDKContext(ctx) if e.EpochNumber == 0 { - return ctx.BlockHeight() == 1 + return sdkCtx.HeaderInfo().Height == 1 } else { - height := uint64(ctx.BlockHeight()) + height := uint64(sdkCtx.HeaderInfo().Height) return e.FirstBlockHeight+e.CurrentEpochInterval == height } } // WithinBoundary checks whether the given height is within this epoch or not func (e Epoch) WithinBoundary(height uint64) bool { - if height < e.FirstBlockHeight || height > uint64(e.LastBlockHeader.Height) { + if height < e.FirstBlockHeight || height > uint64(e.GetLastBlockHeight()) { return false } else { return true @@ -90,8 +106,7 @@ func (e Epoch) ValidateBasic() error { // NewQueuedMessage creates a new QueuedMessage from a wrapped msg // i.e., wrapped -> unwrapped -> QueuedMessage func NewQueuedMessage(blockHeight uint64, blockTime time.Time, txid []byte, msg sdk.Msg) (QueuedMessage, error) { - // marshal the actual msg (MsgDelegate, MsgBeginRedelegate, MsgUndelegate, ...) inside isQueuedMessage_Msg - // TODO (non-urgent): after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation + // marshal the actual msg (MsgDelegate, MsgBeginRedelegate, MsgUndelegate, MsgCancelUnbondingDelegation) inside isQueuedMessage_Msg var qmsg isQueuedMessage_Msg var msgBytes []byte var err error @@ -117,6 +132,13 @@ func NewQueuedMessage(blockHeight uint64, blockTime time.Time, txid []byte, msg qmsg = &QueuedMessage_MsgUndelegate{ MsgUndelegate: msgWithType.Msg, } + case *MsgWrappedCancelUnbondingDelegation: + if msgBytes, err = msgWithType.Msg.Marshal(); err != nil { + return QueuedMessage{}, err + } + qmsg = &QueuedMessage_MsgCancelUnbondingDelegation{ + MsgCancelUnbondingDelegation: msgWithType.Msg, + } case *stakingtypes.MsgCreateValidator: if msgBytes, err = msgWithType.Marshal(); err != nil { return QueuedMessage{}, err @@ -138,15 +160,6 @@ func NewQueuedMessage(blockHeight uint64, blockTime time.Time, txid []byte, msg return queuedMsg, nil } -func (qm QueuedMessage) GetSigners() []sdk.AccAddress { - return qm.UnwrapToSdkMsg().GetSigners() -} - -func (qm QueuedMessage) ValidateBasic() error { - return qm.UnwrapToSdkMsg().ValidateBasic() - -} - // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (qm QueuedMessage) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { var pubKey cryptotypes.PubKey @@ -159,7 +172,6 @@ func (qm QueuedMessage) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error func (qm *QueuedMessage) UnwrapToSdkMsg() sdk.Msg { var unwrappedMsgWithType sdk.Msg - // TODO (non-urgent): after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation switch unwrappedMsg := qm.Msg.(type) { case *QueuedMessage_MsgCreateValidator: unwrappedMsgWithType = unwrappedMsg.MsgCreateValidator @@ -169,6 +181,8 @@ func (qm *QueuedMessage) UnwrapToSdkMsg() sdk.Msg { unwrappedMsgWithType = unwrappedMsg.MsgUndelegate case *QueuedMessage_MsgBeginRedelegate: unwrappedMsgWithType = unwrappedMsg.MsgBeginRedelegate + case *QueuedMessage_MsgCancelUnbondingDelegation: + unwrappedMsgWithType = unwrappedMsg.MsgCancelUnbondingDelegation default: panic(errorsmod.Wrap(ErrInvalidQueuedMessageType, qm.String())) } diff --git a/x/epoching/types/epoching.pb.go b/x/epoching/types/epoching.pb.go index ffa316a79..e63ee0f62 100644 --- a/x/epoching/types/epoching.pb.go +++ b/x/epoching/types/epoching.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - types "github.com/cometbft/cometbft/proto/tendermint/types" - types1 "github.com/cosmos/cosmos-sdk/x/staking/types" + types1 "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/x/staking/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" @@ -71,22 +71,29 @@ func (BondState) EnumDescriptor() ([]byte, []int) { // Epoch is a structure that contains the metadata of an epoch type Epoch struct { - EpochNumber uint64 `protobuf:"varint,1,opt,name=epoch_number,json=epochNumber,proto3" json:"epoch_number,omitempty"` + // epoch_number is the number of this epoch + EpochNumber uint64 `protobuf:"varint,1,opt,name=epoch_number,json=epochNumber,proto3" json:"epoch_number,omitempty"` + // current_epoch_interval is the epoch interval at the time of this epoch CurrentEpochInterval uint64 `protobuf:"varint,2,opt,name=current_epoch_interval,json=currentEpochInterval,proto3" json:"current_epoch_interval,omitempty"` - FirstBlockHeight uint64 `protobuf:"varint,3,opt,name=first_block_height,json=firstBlockHeight,proto3" json:"first_block_height,omitempty"` - // last_block_header is the header of the last block in this epoch. - // Babylon needs to remember the last header of each epoch to complete + // first_block_height is the height of the first block in this epoch + FirstBlockHeight uint64 `protobuf:"varint,3,opt,name=first_block_height,json=firstBlockHeight,proto3" json:"first_block_height,omitempty"` + // last_block_time is the time of the last block in this epoch. + // Babylon needs to remember the last header's time of each epoch to complete // unbonding validators/delegations when a previous epoch's checkpoint is - // finalised. The last_block_header field is nil in the epoch's beginning, and + // finalised. The last_block_time field is nil in the epoch's beginning, and // is set upon the end of this epoch. - LastBlockHeader *types.Header `protobuf:"bytes,4,opt,name=last_block_header,json=lastBlockHeader,proto3" json:"last_block_header,omitempty"` + LastBlockTime *time.Time `protobuf:"bytes,4,opt,name=last_block_time,json=lastBlockTime,proto3,stdtime" json:"last_block_time,omitempty"` // app_hash_root is the Merkle root of all AppHashs in this epoch // It will be used for proving a block is in an epoch AppHashRoot []byte `protobuf:"bytes,5,opt,name=app_hash_root,json=appHashRoot,proto3" json:"app_hash_root,omitempty"` - // sealer_header is the 2nd header of the next epoch - // This validator set has generated a BLS multisig on `last_commit_hash` of - // the sealer header - SealerHeader *types.Header `protobuf:"bytes,6,opt,name=sealer_header,json=sealerHeader,proto3" json:"sealer_header,omitempty"` + // sealer is the last block of the sealed epoch + // sealer_app_hash points to the sealer but stored in the 1st header + // of the next epoch + SealerAppHash []byte `protobuf:"bytes,6,opt,name=sealer_app_hash,json=sealerAppHash,proto3" json:"sealer_app_hash,omitempty"` + // sealer_block_hash is the hash of the sealer + // the validator set has generated a BLS multisig on the hash, + // i.e., hash of the last block in the epoch + SealerBlockHash []byte `protobuf:"bytes,7,opt,name=sealer_block_hash,json=sealerBlockHash,proto3" json:"sealer_block_hash,omitempty"` } func (m *Epoch) Reset() { *m = Epoch{} } @@ -143,9 +150,9 @@ func (m *Epoch) GetFirstBlockHeight() uint64 { return 0 } -func (m *Epoch) GetLastBlockHeader() *types.Header { +func (m *Epoch) GetLastBlockTime() *time.Time { if m != nil { - return m.LastBlockHeader + return m.LastBlockTime } return nil } @@ -157,15 +164,22 @@ func (m *Epoch) GetAppHashRoot() []byte { return nil } -func (m *Epoch) GetSealerHeader() *types.Header { +func (m *Epoch) GetSealerAppHash() []byte { if m != nil { - return m.SealerHeader + return m.SealerAppHash + } + return nil +} + +func (m *Epoch) GetSealerBlockHash() []byte { + if m != nil { + return m.SealerBlockHash } return nil } // QueuedMessage is a message that can change the validator set and is delayed -// to the epoch boundary +// to the end of an epoch type QueuedMessage struct { // tx_id is the ID of the tx that contains the message TxId []byte `protobuf:"bytes,1,opt,name=tx_id,json=txId,proto3" json:"tx_id,omitempty"` @@ -183,6 +197,7 @@ type QueuedMessage struct { // *QueuedMessage_MsgDelegate // *QueuedMessage_MsgUndelegate // *QueuedMessage_MsgBeginRedelegate + // *QueuedMessage_MsgCancelUnbondingDelegation Msg isQueuedMessage_Msg `protobuf_oneof:"msg"` } @@ -226,22 +241,26 @@ type isQueuedMessage_Msg interface { } type QueuedMessage_MsgCreateValidator struct { - MsgCreateValidator *types1.MsgCreateValidator `protobuf:"bytes,5,opt,name=msg_create_validator,json=msgCreateValidator,proto3,oneof" json:"msg_create_validator,omitempty"` + MsgCreateValidator *types.MsgCreateValidator `protobuf:"bytes,5,opt,name=msg_create_validator,json=msgCreateValidator,proto3,oneof" json:"msg_create_validator,omitempty"` } type QueuedMessage_MsgDelegate struct { - MsgDelegate *types1.MsgDelegate `protobuf:"bytes,6,opt,name=msg_delegate,json=msgDelegate,proto3,oneof" json:"msg_delegate,omitempty"` + MsgDelegate *types.MsgDelegate `protobuf:"bytes,6,opt,name=msg_delegate,json=msgDelegate,proto3,oneof" json:"msg_delegate,omitempty"` } type QueuedMessage_MsgUndelegate struct { - MsgUndelegate *types1.MsgUndelegate `protobuf:"bytes,7,opt,name=msg_undelegate,json=msgUndelegate,proto3,oneof" json:"msg_undelegate,omitempty"` + MsgUndelegate *types.MsgUndelegate `protobuf:"bytes,7,opt,name=msg_undelegate,json=msgUndelegate,proto3,oneof" json:"msg_undelegate,omitempty"` } type QueuedMessage_MsgBeginRedelegate struct { - MsgBeginRedelegate *types1.MsgBeginRedelegate `protobuf:"bytes,8,opt,name=msg_begin_redelegate,json=msgBeginRedelegate,proto3,oneof" json:"msg_begin_redelegate,omitempty"` + MsgBeginRedelegate *types.MsgBeginRedelegate `protobuf:"bytes,8,opt,name=msg_begin_redelegate,json=msgBeginRedelegate,proto3,oneof" json:"msg_begin_redelegate,omitempty"` +} +type QueuedMessage_MsgCancelUnbondingDelegation struct { + MsgCancelUnbondingDelegation *types.MsgCancelUnbondingDelegation `protobuf:"bytes,9,opt,name=msg_cancel_unbonding_delegation,json=msgCancelUnbondingDelegation,proto3,oneof" json:"msg_cancel_unbonding_delegation,omitempty"` } -func (*QueuedMessage_MsgCreateValidator) isQueuedMessage_Msg() {} -func (*QueuedMessage_MsgDelegate) isQueuedMessage_Msg() {} -func (*QueuedMessage_MsgUndelegate) isQueuedMessage_Msg() {} -func (*QueuedMessage_MsgBeginRedelegate) isQueuedMessage_Msg() {} +func (*QueuedMessage_MsgCreateValidator) isQueuedMessage_Msg() {} +func (*QueuedMessage_MsgDelegate) isQueuedMessage_Msg() {} +func (*QueuedMessage_MsgUndelegate) isQueuedMessage_Msg() {} +func (*QueuedMessage_MsgBeginRedelegate) isQueuedMessage_Msg() {} +func (*QueuedMessage_MsgCancelUnbondingDelegation) isQueuedMessage_Msg() {} func (m *QueuedMessage) GetMsg() isQueuedMessage_Msg { if m != nil { @@ -278,34 +297,41 @@ func (m *QueuedMessage) GetBlockTime() *time.Time { return nil } -func (m *QueuedMessage) GetMsgCreateValidator() *types1.MsgCreateValidator { +func (m *QueuedMessage) GetMsgCreateValidator() *types.MsgCreateValidator { if x, ok := m.GetMsg().(*QueuedMessage_MsgCreateValidator); ok { return x.MsgCreateValidator } return nil } -func (m *QueuedMessage) GetMsgDelegate() *types1.MsgDelegate { +func (m *QueuedMessage) GetMsgDelegate() *types.MsgDelegate { if x, ok := m.GetMsg().(*QueuedMessage_MsgDelegate); ok { return x.MsgDelegate } return nil } -func (m *QueuedMessage) GetMsgUndelegate() *types1.MsgUndelegate { +func (m *QueuedMessage) GetMsgUndelegate() *types.MsgUndelegate { if x, ok := m.GetMsg().(*QueuedMessage_MsgUndelegate); ok { return x.MsgUndelegate } return nil } -func (m *QueuedMessage) GetMsgBeginRedelegate() *types1.MsgBeginRedelegate { +func (m *QueuedMessage) GetMsgBeginRedelegate() *types.MsgBeginRedelegate { if x, ok := m.GetMsg().(*QueuedMessage_MsgBeginRedelegate); ok { return x.MsgBeginRedelegate } return nil } +func (m *QueuedMessage) GetMsgCancelUnbondingDelegation() *types.MsgCancelUnbondingDelegation { + if x, ok := m.GetMsg().(*QueuedMessage_MsgCancelUnbondingDelegation); ok { + return x.MsgCancelUnbondingDelegation + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*QueuedMessage) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -313,6 +339,7 @@ func (*QueuedMessage) XXX_OneofWrappers() []interface{} { (*QueuedMessage_MsgDelegate)(nil), (*QueuedMessage_MsgUndelegate)(nil), (*QueuedMessage_MsgBeginRedelegate)(nil), + (*QueuedMessage_MsgCancelUnbondingDelegation)(nil), } } @@ -488,10 +515,11 @@ func (m *ValidatorLifecycle) GetValLife() []*ValStateUpdate { // DelegationStateUpdate is the message that records a state update of a // delegation type DelegationStateUpdate struct { - State BondState `protobuf:"varint,1,opt,name=state,proto3,enum=babylon.epoching.v1.BondState" json:"state,omitempty"` - ValAddr string `protobuf:"bytes,2,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` - BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` - BlockTime *time.Time `protobuf:"bytes,4,opt,name=block_time,json=blockTime,proto3,stdtime" json:"block_time,omitempty"` + State BondState `protobuf:"varint,1,opt,name=state,proto3,enum=babylon.epoching.v1.BondState" json:"state,omitempty"` + ValAddr string `protobuf:"bytes,2,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` + Amount *types1.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + BlockHeight uint64 `protobuf:"varint,4,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + BlockTime *time.Time `protobuf:"bytes,5,opt,name=block_time,json=blockTime,proto3,stdtime" json:"block_time,omitempty"` } func (m *DelegationStateUpdate) Reset() { *m = DelegationStateUpdate{} } @@ -541,6 +569,13 @@ func (m *DelegationStateUpdate) GetValAddr() string { return "" } +func (m *DelegationStateUpdate) GetAmount() *types1.Coin { + if m != nil { + return m.Amount + } + return nil +} + func (m *DelegationStateUpdate) GetBlockHeight() uint64 { if m != nil { return m.BlockHeight @@ -681,61 +716,65 @@ func init() { } var fileDescriptor_2f2f209d5311f84c = []byte{ - // 864 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0x8e, 0xf3, 0xd3, 0x36, 0x27, 0x49, 0xc9, 0x4e, 0xbb, 0x28, 0x54, 0x28, 0x2d, 0x5e, 0x21, - 0x55, 0x15, 0xb2, 0x69, 0x59, 0xb8, 0x03, 0xb4, 0xd9, 0x44, 0xa4, 0x68, 0x9b, 0x15, 0x66, 0xdb, - 0x0b, 0x2e, 0xb0, 0xc6, 0xf6, 0xa9, 0x63, 0xad, 0xed, 0xb1, 0x3c, 0x93, 0x6c, 0x7a, 0xc7, 0x23, - 0xec, 0x73, 0xf0, 0x02, 0xbc, 0x02, 0x17, 0x5c, 0xec, 0x25, 0x77, 0xa0, 0xf6, 0x45, 0xd0, 0xcc, - 0x38, 0x7f, 0x6c, 0xb4, 0x05, 0x21, 0x71, 0x13, 0xcd, 0x39, 0xe7, 0x3b, 0xdf, 0x9c, 0xf3, 0xe9, - 0x1b, 0x07, 0x4c, 0x8f, 0x7a, 0x37, 0x31, 0x4b, 0x6d, 0xcc, 0x98, 0x3f, 0x8e, 0xd2, 0xd0, 0x9e, - 0x9e, 0x2e, 0xce, 0x56, 0x96, 0x33, 0xc1, 0xc8, 0x5e, 0x81, 0xb1, 0x16, 0xf9, 0xe9, 0xe9, 0xc1, - 0x61, 0xc8, 0x58, 0x18, 0xa3, 0xad, 0x20, 0xde, 0xe4, 0xda, 0x16, 0x51, 0x82, 0x5c, 0xd0, 0x24, - 0xd3, 0x5d, 0x07, 0xfb, 0x21, 0x0b, 0x99, 0x3a, 0xda, 0xf2, 0x54, 0x64, 0x3f, 0x14, 0x98, 0x06, - 0x98, 0x27, 0x51, 0x2a, 0x6c, 0x71, 0x93, 0x21, 0xd7, 0xbf, 0x45, 0xf5, 0xd0, 0x67, 0x3c, 0x61, - 0xdc, 0xe6, 0x82, 0xbe, 0xd4, 0xb3, 0x78, 0x28, 0xe8, 0xa9, 0x2d, 0x66, 0x1a, 0x60, 0xfe, 0x52, - 0x86, 0xda, 0x40, 0x4e, 0x41, 0x3e, 0x82, 0xa6, 0x1a, 0xc7, 0x4d, 0x27, 0x89, 0x87, 0x79, 0xc7, - 0x38, 0x32, 0x8e, 0xab, 0x4e, 0x43, 0xe5, 0x46, 0x2a, 0x45, 0x1e, 0xc3, 0xfb, 0xfe, 0x24, 0xcf, - 0x31, 0x15, 0xae, 0x86, 0x46, 0xa9, 0xc0, 0x7c, 0x4a, 0xe3, 0x4e, 0x59, 0x81, 0xf7, 0x8b, 0xaa, - 0x22, 0x3c, 0x2f, 0x6a, 0xe4, 0x13, 0x20, 0xd7, 0x51, 0xce, 0x85, 0xeb, 0xc5, 0xcc, 0x7f, 0xe9, - 0x8e, 0x31, 0x0a, 0xc7, 0xa2, 0x53, 0x51, 0x1d, 0x6d, 0x55, 0xe9, 0xc9, 0xc2, 0x50, 0xe5, 0x49, - 0x1f, 0x1e, 0xc4, 0x74, 0x05, 0x4c, 0x03, 0xcc, 0x3b, 0xd5, 0x23, 0xe3, 0xb8, 0x71, 0xd6, 0xb1, - 0x96, 0xbb, 0x5a, 0x7a, 0xcb, 0xa1, 0xaa, 0x3b, 0xef, 0xc9, 0x96, 0x82, 0x45, 0x26, 0x88, 0x09, - 0x2d, 0x9a, 0x65, 0xee, 0x98, 0xf2, 0xb1, 0x9b, 0x33, 0x26, 0x3a, 0xb5, 0x23, 0xe3, 0xb8, 0xe9, - 0x34, 0x68, 0x96, 0x0d, 0x29, 0x1f, 0x3b, 0x8c, 0x09, 0xf2, 0x25, 0xb4, 0x38, 0xd2, 0x18, 0xf3, - 0xf9, 0x2d, 0x5b, 0xf7, 0xdc, 0xd2, 0xd4, 0x70, 0x1d, 0x99, 0x3f, 0x55, 0xa1, 0xf5, 0xdd, 0x04, - 0x27, 0x18, 0x5c, 0x20, 0xe7, 0x34, 0x44, 0xb2, 0x07, 0x35, 0x31, 0x73, 0xa3, 0x40, 0x49, 0xd7, - 0x74, 0xaa, 0x62, 0x76, 0x1e, 0x90, 0x87, 0xb0, 0x95, 0xf0, 0x50, 0x66, 0xcb, 0x2a, 0x5b, 0x4b, - 0x78, 0x78, 0x1e, 0x48, 0xb5, 0x37, 0xc8, 0xd1, 0xf0, 0x56, 0x94, 0xf8, 0x1a, 0x40, 0x43, 0xa4, - 0x11, 0x0a, 0x09, 0x0e, 0x2c, 0xed, 0x12, 0x6b, 0xee, 0x12, 0xeb, 0xc5, 0xdc, 0x25, 0xbd, 0xea, - 0xeb, 0x3f, 0x0e, 0x0d, 0xa7, 0xae, 0x7a, 0x64, 0x96, 0xfc, 0x08, 0xfb, 0xf2, 0x6a, 0x3f, 0x47, - 0x2a, 0xd0, 0x9d, 0xd2, 0x38, 0x0a, 0xa8, 0x60, 0xb9, 0xd2, 0xa2, 0x71, 0x76, 0x62, 0x69, 0x6f, - 0x58, 0x85, 0x37, 0xac, 0xc2, 0x1b, 0xd6, 0x05, 0x0f, 0x9f, 0xaa, 0x96, 0xab, 0x79, 0xc7, 0xb0, - 0xe4, 0x90, 0xe4, 0xad, 0x2c, 0x19, 0x42, 0x53, 0xf2, 0x07, 0x18, 0x63, 0x48, 0x05, 0x16, 0xfa, - 0x3d, 0x7a, 0x07, 0x6f, 0xbf, 0x80, 0x0e, 0x4b, 0x4e, 0x23, 0x59, 0x86, 0x64, 0x04, 0xbb, 0x92, - 0x69, 0x92, 0x2e, 0xb8, 0xb6, 0x15, 0xd7, 0xc7, 0xef, 0xe0, 0xba, 0x5c, 0x80, 0x87, 0x25, 0xa7, - 0x95, 0xac, 0x26, 0xe6, 0x9b, 0x7b, 0x18, 0x46, 0xa9, 0x9b, 0xe3, 0x82, 0x75, 0xe7, 0xde, 0xcd, - 0x7b, 0xb2, 0xc5, 0xc1, 0x15, 0x6a, 0xb9, 0xf9, 0xdf, 0xb2, 0xbd, 0x1a, 0x54, 0x12, 0x1e, 0x9a, - 0x29, 0x3c, 0x58, 0x73, 0xc0, 0xb3, 0x88, 0x8b, 0x7f, 0xf2, 0x8e, 0xbe, 0x80, 0x6a, 0xc2, 0x43, - 0xde, 0x29, 0x1f, 0x55, 0x8e, 0x1b, 0x67, 0xa6, 0xb5, 0xe1, 0x73, 0x60, 0xad, 0x11, 0x3b, 0x0a, - 0x6f, 0xfe, 0x6c, 0xc0, 0xee, 0x15, 0x8d, 0xbf, 0x17, 0x54, 0xe0, 0x65, 0x16, 0xc8, 0x4d, 0x1f, - 0x43, 0x8d, 0xcb, 0x50, 0x5d, 0xb3, 0x7b, 0xd6, 0xdd, 0xc8, 0xd5, 0x63, 0x69, 0xa0, 0x9a, 0x1c, - 0x0d, 0x7e, 0xcb, 0x7d, 0xe5, 0xfb, 0xdc, 0x57, 0xf9, 0xd7, 0xee, 0x33, 0x19, 0x90, 0x85, 0x55, - 0x9e, 0x45, 0xd7, 0xe8, 0xdf, 0xf8, 0x31, 0x92, 0x0f, 0x60, 0x67, 0x4a, 0x63, 0x97, 0x06, 0x81, - 0x56, 0xa6, 0xee, 0x6c, 0x4f, 0x69, 0xfc, 0x24, 0x08, 0x72, 0xf2, 0x95, 0x2e, 0xc5, 0xd1, 0x35, - 0x16, 0xca, 0x3c, 0xda, 0xb8, 0xcd, 0xba, 0x02, 0xaa, 0x5f, 0xf2, 0x9b, 0xbf, 0x19, 0xf0, 0xb0, - 0x70, 0x54, 0xc4, 0xd2, 0xff, 0x2e, 0xd2, 0xea, 0xa8, 0xe5, 0xf5, 0x51, 0xff, 0x87, 0xd7, 0x6b, - 0xbe, 0x82, 0xbd, 0xe5, 0x36, 0x6b, 0x02, 0x06, 0xb8, 0x2e, 0x60, 0x80, 0x7a, 0xaa, 0x81, 0x2e, - 0xad, 0x08, 0x78, 0xb2, 0x71, 0xd3, 0x8d, 0x22, 0x29, 0x1a, 0xa5, 0xe3, 0xe7, 0x50, 0x5f, 0xbe, - 0x71, 0x02, 0xd5, 0xc5, 0x55, 0x4d, 0x47, 0x9d, 0xc9, 0x3e, 0xd4, 0x32, 0xf6, 0x0a, 0xb5, 0x2a, - 0x15, 0x47, 0x07, 0x27, 0x23, 0xa8, 0x2f, 0x24, 0x24, 0x0d, 0xd8, 0x7e, 0xea, 0x0c, 0x9e, 0xbc, - 0x18, 0xf4, 0xdb, 0x25, 0x02, 0xb0, 0xd5, 0x7b, 0x3e, 0xea, 0x0f, 0xfa, 0x6d, 0x83, 0xb4, 0xa0, - 0x7e, 0x39, 0x92, 0xd1, 0xf9, 0xe8, 0x9b, 0x76, 0x99, 0x34, 0x61, 0x47, 0x87, 0x83, 0x7e, 0xbb, - 0x22, 0xbb, 0x9c, 0xc1, 0xc5, 0xf3, 0xab, 0x41, 0xbf, 0x5d, 0xed, 0x7d, 0xfb, 0xeb, 0x6d, 0xd7, - 0x78, 0x73, 0xdb, 0x35, 0xfe, 0xbc, 0xed, 0x1a, 0xaf, 0xef, 0xba, 0xa5, 0x37, 0x77, 0xdd, 0xd2, - 0xef, 0x77, 0xdd, 0xd2, 0x0f, 0x9f, 0x86, 0x91, 0x18, 0x4f, 0x3c, 0xcb, 0x67, 0x89, 0x5d, 0xec, - 0xe7, 0x8f, 0x69, 0x94, 0xce, 0x03, 0x7b, 0xb6, 0xfc, 0xf3, 0x55, 0x1f, 0x70, 0x6f, 0x4b, 0x09, - 0xfe, 0xd9, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfb, 0xf0, 0x87, 0xbf, 0x9d, 0x07, 0x00, 0x00, + // 927 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x96, 0xdf, 0x6e, 0xdb, 0x36, + 0x14, 0xc6, 0x2d, 0xff, 0x49, 0xe2, 0x63, 0x3b, 0x75, 0x99, 0x74, 0x70, 0x83, 0xc1, 0xc9, 0x5c, + 0x6c, 0x08, 0x82, 0x41, 0x5e, 0xbc, 0x6c, 0x97, 0x1b, 0xe2, 0xd8, 0x98, 0x33, 0x34, 0x2e, 0xc6, + 0x35, 0xb9, 0xd8, 0xc5, 0x04, 0x4a, 0x62, 0x64, 0xa1, 0x12, 0x29, 0x88, 0x94, 0x9b, 0x5c, 0xec, + 0x1d, 0xfa, 0x1c, 0x7b, 0x92, 0x5d, 0xe6, 0x72, 0x77, 0x1b, 0x92, 0x07, 0xe9, 0x40, 0x4a, 0x96, + 0xed, 0xc5, 0x48, 0xd6, 0xf5, 0x8e, 0x3c, 0xe7, 0x3b, 0x1f, 0x79, 0x7e, 0x3a, 0x26, 0x0c, 0x1d, + 0x9b, 0xd8, 0xd7, 0x01, 0x67, 0x5d, 0x1a, 0x71, 0x67, 0xe2, 0x33, 0xaf, 0x3b, 0x3d, 0xcc, 0xd7, + 0x66, 0x14, 0x73, 0xc9, 0xd1, 0x56, 0xa6, 0x31, 0xf3, 0xf8, 0xf4, 0x70, 0x67, 0xd7, 0xe3, 0xdc, + 0x0b, 0x68, 0x57, 0x4b, 0xec, 0xe4, 0xb2, 0x2b, 0xfd, 0x90, 0x0a, 0x49, 0xc2, 0x28, 0xad, 0xda, + 0xd9, 0xf6, 0xb8, 0xc7, 0xf5, 0xb2, 0xab, 0x56, 0x59, 0x74, 0xd7, 0xe1, 0x22, 0xe4, 0xa2, 0x2b, + 0x24, 0x79, 0x93, 0x9e, 0x66, 0x53, 0x49, 0x0e, 0xbb, 0xf2, 0x2a, 0x13, 0xb4, 0x33, 0x81, 0x4d, + 0x04, 0xcd, 0xb3, 0x0e, 0xf7, 0x59, 0x9a, 0xef, 0xdc, 0x14, 0xa1, 0x32, 0x54, 0xf7, 0x40, 0x9f, + 0x41, 0x5d, 0x5f, 0xc8, 0x62, 0x49, 0x68, 0xd3, 0xb8, 0x65, 0xec, 0x19, 0xfb, 0x65, 0x5c, 0xd3, + 0xb1, 0xb1, 0x0e, 0xa1, 0x23, 0xf8, 0xc4, 0x49, 0xe2, 0x98, 0x32, 0x69, 0xa5, 0x52, 0x9f, 0x49, + 0x1a, 0x4f, 0x49, 0xd0, 0x2a, 0x6a, 0xf1, 0x76, 0x96, 0xd5, 0x86, 0xa7, 0x59, 0x0e, 0x7d, 0x09, + 0xe8, 0xd2, 0x8f, 0x85, 0xb4, 0xec, 0x80, 0x3b, 0x6f, 0xac, 0x09, 0xf5, 0xbd, 0x89, 0x6c, 0x95, + 0x74, 0x45, 0x53, 0x67, 0xfa, 0x2a, 0x31, 0xd2, 0x71, 0x34, 0x82, 0x27, 0x01, 0xc9, 0xc5, 0x8a, + 0x42, 0xab, 0xbc, 0x67, 0xec, 0xd7, 0x7a, 0x3b, 0x66, 0x8a, 0xc8, 0x9c, 0x21, 0x32, 0x5f, 0xcf, + 0x10, 0xf5, 0xcb, 0xef, 0xfe, 0xda, 0x35, 0x70, 0x43, 0x15, 0x6a, 0x2f, 0x95, 0x41, 0x1d, 0x68, + 0x90, 0x28, 0xb2, 0x26, 0x44, 0x4c, 0xac, 0x98, 0x73, 0xd9, 0xaa, 0xec, 0x19, 0xfb, 0x75, 0x5c, + 0x23, 0x51, 0x34, 0x22, 0x62, 0x82, 0x39, 0x97, 0xe8, 0x0b, 0x78, 0x22, 0x28, 0x09, 0x68, 0x6c, + 0xcd, 0xa4, 0xad, 0x35, 0xad, 0x6a, 0xa4, 0xe1, 0xe3, 0x54, 0x8b, 0x0e, 0xe0, 0x69, 0xa6, 0xcb, + 0x9a, 0x50, 0xca, 0x75, 0xad, 0xcc, 0x0c, 0xd2, 0x1e, 0x88, 0x98, 0x74, 0xde, 0x97, 0xa1, 0xf1, + 0x53, 0x42, 0x13, 0xea, 0x9e, 0x51, 0x21, 0x88, 0x47, 0xd1, 0x16, 0x54, 0xe4, 0x95, 0xe5, 0xbb, + 0x9a, 0x69, 0x1d, 0x97, 0xe5, 0xd5, 0xa9, 0x8b, 0x9e, 0xc1, 0x5a, 0x28, 0x3c, 0x15, 0x2d, 0xea, + 0x68, 0x25, 0x14, 0xde, 0xa9, 0xab, 0x3e, 0xc3, 0x0a, 0x4e, 0x35, 0x7b, 0x01, 0xd1, 0xf7, 0x00, + 0xff, 0x83, 0x4e, 0xd5, 0xce, 0xc9, 0xfc, 0x0a, 0xdb, 0xea, 0x68, 0x27, 0xa6, 0x44, 0x52, 0x6b, + 0x4a, 0x02, 0xdf, 0x25, 0x92, 0xc7, 0x1a, 0x50, 0xad, 0x77, 0x60, 0xa6, 0x33, 0x63, 0x66, 0x43, + 0x65, 0x66, 0x63, 0x63, 0x9e, 0x09, 0xef, 0x44, 0x97, 0x5c, 0xcc, 0x2a, 0x46, 0x05, 0x8c, 0xc2, + 0x7b, 0x51, 0x34, 0x82, 0xba, 0xf2, 0x77, 0x69, 0x40, 0x3d, 0x22, 0xa9, 0x46, 0x5a, 0xeb, 0xbd, + 0x78, 0xc0, 0x77, 0x90, 0x49, 0x47, 0x05, 0x5c, 0x0b, 0xe7, 0x5b, 0x34, 0x86, 0x4d, 0xe5, 0x94, + 0xb0, 0xdc, 0x6b, 0x5d, 0x7b, 0x7d, 0xfe, 0x80, 0xd7, 0x79, 0x2e, 0x1e, 0x15, 0x70, 0x23, 0x5c, + 0x0c, 0xcc, 0x3a, 0xb7, 0xa9, 0xe7, 0x33, 0x2b, 0xa6, 0xb9, 0xeb, 0xc6, 0xa3, 0x9d, 0xf7, 0x55, + 0x09, 0xa6, 0x0b, 0xd6, 0xaa, 0xf3, 0x7f, 0x45, 0xd1, 0x6f, 0xb0, 0xab, 0xc9, 0x12, 0xe6, 0xd0, + 0xc0, 0x4a, 0x98, 0xcd, 0x99, 0xeb, 0xb3, 0x1c, 0x85, 0xcf, 0x59, 0xab, 0xaa, 0x8f, 0x3a, 0x7a, + 0x08, 0xb2, 0xae, 0x3e, 0x9f, 0x15, 0x0f, 0xf2, 0xda, 0x51, 0x01, 0x7f, 0x1a, 0x3e, 0x90, 0xef, + 0x57, 0xa0, 0x14, 0x0a, 0xaf, 0xc3, 0xe0, 0xe9, 0xd2, 0x00, 0xbe, 0xf4, 0x85, 0xfc, 0x2f, 0xbf, + 0xef, 0x6f, 0xa1, 0x1c, 0x0a, 0x4f, 0xb4, 0x8a, 0x7b, 0xa5, 0xfd, 0x5a, 0xaf, 0x63, 0xae, 0x78, + 0xa8, 0xcc, 0x25, 0x63, 0xac, 0xf5, 0x9d, 0xdf, 0x0d, 0xd8, 0xbc, 0x20, 0xc1, 0xcf, 0x92, 0x48, + 0x7a, 0x1e, 0xb9, 0x0a, 0xc4, 0x11, 0x54, 0x84, 0xda, 0xea, 0x63, 0x36, 0x7b, 0xed, 0x95, 0x5e, + 0x7d, 0xce, 0x5c, 0x5d, 0x84, 0x53, 0xf1, 0xbd, 0xe1, 0x2f, 0x3e, 0x36, 0xfc, 0xa5, 0x0f, 0x1e, + 0xfe, 0x0e, 0x07, 0x94, 0x4f, 0xea, 0x4b, 0xff, 0x92, 0x3a, 0xd7, 0x4e, 0x40, 0xd1, 0x73, 0xd8, + 0x98, 0x92, 0xc0, 0x22, 0xae, 0x9b, 0x92, 0xa9, 0xe2, 0xf5, 0x29, 0x09, 0x8e, 0x5d, 0x37, 0x46, + 0xdf, 0xa5, 0xa9, 0xc0, 0xbf, 0xa4, 0x19, 0x99, 0x17, 0x2b, 0xbb, 0x59, 0x26, 0xa0, 0xeb, 0x95, + 0x7f, 0xe7, 0xbd, 0x01, 0xcf, 0xe6, 0xdf, 0xe8, 0xe3, 0x21, 0x2d, 0x5e, 0xb5, 0xb8, 0x7c, 0xd5, + 0x43, 0x58, 0x23, 0x21, 0x4f, 0x98, 0xcc, 0xc0, 0x3c, 0x9f, 0x4d, 0x99, 0x7a, 0xfe, 0xf3, 0x11, + 0x3b, 0xe1, 0x3e, 0xc3, 0x99, 0xf0, 0x1e, 0xf2, 0xf2, 0x63, 0xc8, 0x2b, 0x1f, 0x8e, 0xfc, 0x2d, + 0x6c, 0xcd, 0x01, 0x2c, 0x31, 0x77, 0xe9, 0x32, 0x73, 0x97, 0xa6, 0x8d, 0x0c, 0xd3, 0xd4, 0x02, + 0xf3, 0x83, 0x95, 0x70, 0x56, 0x72, 0xd5, 0x36, 0x1a, 0xfd, 0x37, 0x50, 0x9d, 0xbf, 0x4a, 0x08, + 0xca, 0xf9, 0x51, 0x75, 0xac, 0xd7, 0x68, 0x1b, 0x2a, 0x11, 0x7f, 0x4b, 0x53, 0x90, 0x25, 0x9c, + 0x6e, 0x0e, 0xc6, 0x50, 0xcd, 0xa9, 0xa3, 0x1a, 0xac, 0x9f, 0xe0, 0xe1, 0xf1, 0xeb, 0xe1, 0xa0, + 0x59, 0x40, 0x00, 0x6b, 0xfd, 0x57, 0xe3, 0xc1, 0x70, 0xd0, 0x34, 0x50, 0x03, 0xaa, 0xe7, 0x63, + 0xb5, 0x3b, 0x1d, 0xff, 0xd0, 0x2c, 0xa2, 0x3a, 0x6c, 0xa4, 0xdb, 0xe1, 0xa0, 0x59, 0x52, 0x55, + 0x78, 0x78, 0xf6, 0xea, 0x62, 0x38, 0x68, 0x96, 0xfb, 0x3f, 0xfe, 0x71, 0xdb, 0x36, 0x6e, 0x6e, + 0xdb, 0xc6, 0xdf, 0xb7, 0x6d, 0xe3, 0xdd, 0x5d, 0xbb, 0x70, 0x73, 0xd7, 0x2e, 0xfc, 0x79, 0xd7, + 0x2e, 0xfc, 0xf2, 0x95, 0xe7, 0xcb, 0x49, 0x62, 0x9b, 0x0e, 0x0f, 0xbb, 0x59, 0x7f, 0xce, 0x84, + 0xf8, 0x6c, 0xb6, 0xe9, 0x5e, 0xcd, 0xff, 0x49, 0xc8, 0xeb, 0x88, 0x0a, 0x7b, 0x4d, 0x03, 0xff, + 0xfa, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x41, 0x06, 0xf6, 0x6a, 0x08, 0x00, 0x00, } func (m *Epoch) Marshal() (dAtA []byte, err error) { @@ -758,15 +797,17 @@ func (m *Epoch) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.SealerHeader != nil { - { - size, err := m.SealerHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintEpoching(dAtA, i, uint64(size)) - } + if len(m.SealerBlockHash) > 0 { + i -= len(m.SealerBlockHash) + copy(dAtA[i:], m.SealerBlockHash) + i = encodeVarintEpoching(dAtA, i, uint64(len(m.SealerBlockHash))) + i-- + dAtA[i] = 0x3a + } + if len(m.SealerAppHash) > 0 { + i -= len(m.SealerAppHash) + copy(dAtA[i:], m.SealerAppHash) + i = encodeVarintEpoching(dAtA, i, uint64(len(m.SealerAppHash))) i-- dAtA[i] = 0x32 } @@ -777,15 +818,13 @@ func (m *Epoch) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if m.LastBlockHeader != nil { - { - size, err := m.LastBlockHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintEpoching(dAtA, i, uint64(size)) + if m.LastBlockTime != nil { + n1, err1 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.LastBlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.LastBlockTime):]) + if err1 != nil { + return 0, err1 } + i -= n1 + i = encodeVarintEpoching(dAtA, i, uint64(n1)) i-- dAtA[i] = 0x22 } @@ -837,12 +876,12 @@ func (m *QueuedMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if m.BlockTime != nil { - n3, err3 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.BlockTime):]) - if err3 != nil { - return 0, err3 + n2, err2 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.BlockTime):]) + if err2 != nil { + return 0, err2 } - i -= n3 - i = encodeVarintEpoching(dAtA, i, uint64(n3)) + i -= n2 + i = encodeVarintEpoching(dAtA, i, uint64(n2)) i-- dAtA[i] = 0x22 } @@ -952,6 +991,27 @@ func (m *QueuedMessage_MsgBeginRedelegate) MarshalToSizedBuffer(dAtA []byte) (in } return len(dAtA) - i, nil } +func (m *QueuedMessage_MsgCancelUnbondingDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueuedMessage_MsgCancelUnbondingDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.MsgCancelUnbondingDelegation != nil { + { + size, err := m.MsgCancelUnbondingDelegation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEpoching(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} func (m *QueuedMessageList) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1109,12 +1169,24 @@ func (m *DelegationStateUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= n9 i = encodeVarintEpoching(dAtA, i, uint64(n9)) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if m.BlockHeight != 0 { i = encodeVarintEpoching(dAtA, i, uint64(m.BlockHeight)) i-- - dAtA[i] = 0x18 + dAtA[i] = 0x20 + } + if m.Amount != nil { + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEpoching(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } if len(m.ValAddr) > 0 { i -= len(m.ValAddr) @@ -1236,16 +1308,20 @@ func (m *Epoch) Size() (n int) { if m.FirstBlockHeight != 0 { n += 1 + sovEpoching(uint64(m.FirstBlockHeight)) } - if m.LastBlockHeader != nil { - l = m.LastBlockHeader.Size() + if m.LastBlockTime != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.LastBlockTime) n += 1 + l + sovEpoching(uint64(l)) } l = len(m.AppHashRoot) if l > 0 { n += 1 + l + sovEpoching(uint64(l)) } - if m.SealerHeader != nil { - l = m.SealerHeader.Size() + l = len(m.SealerAppHash) + if l > 0 { + n += 1 + l + sovEpoching(uint64(l)) + } + l = len(m.SealerBlockHash) + if l > 0 { n += 1 + l + sovEpoching(uint64(l)) } return n @@ -1326,6 +1402,18 @@ func (m *QueuedMessage_MsgBeginRedelegate) Size() (n int) { } return n } +func (m *QueuedMessage_MsgCancelUnbondingDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MsgCancelUnbondingDelegation != nil { + l = m.MsgCancelUnbondingDelegation.Size() + n += 1 + l + sovEpoching(uint64(l)) + } + return n +} func (m *QueuedMessageList) Size() (n int) { if m == nil { return 0 @@ -1395,6 +1483,10 @@ func (m *DelegationStateUpdate) Size() (n int) { if l > 0 { n += 1 + l + sovEpoching(uint64(l)) } + if m.Amount != nil { + l = m.Amount.Size() + n += 1 + l + sovEpoching(uint64(l)) + } if m.BlockHeight != 0 { n += 1 + sovEpoching(uint64(m.BlockHeight)) } @@ -1534,7 +1626,7 @@ func (m *Epoch) Unmarshal(dAtA []byte) error { } case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastBlockHeader", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LastBlockTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1561,10 +1653,10 @@ func (m *Epoch) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.LastBlockHeader == nil { - m.LastBlockHeader = &types.Header{} + if m.LastBlockTime == nil { + m.LastBlockTime = new(time.Time) } - if err := m.LastBlockHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(m.LastBlockTime, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1604,9 +1696,9 @@ func (m *Epoch) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SealerHeader", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SealerAppHash", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEpoching @@ -1616,26 +1708,58 @@ func (m *Epoch) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthEpoching } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthEpoching } if postIndex > l { return io.ErrUnexpectedEOF } - if m.SealerHeader == nil { - m.SealerHeader = &types.Header{} + m.SealerAppHash = append(m.SealerAppHash[:0], dAtA[iNdEx:postIndex]...) + if m.SealerAppHash == nil { + m.SealerAppHash = []byte{} } - if err := m.SealerHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SealerBlockHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEpoching + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEpoching + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEpoching + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SealerBlockHash = append(m.SealerBlockHash[:0], dAtA[iNdEx:postIndex]...) + if m.SealerBlockHash == nil { + m.SealerBlockHash = []byte{} } iNdEx = postIndex default: @@ -1840,7 +1964,7 @@ func (m *QueuedMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &types1.MsgCreateValidator{} + v := &types.MsgCreateValidator{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1875,7 +1999,7 @@ func (m *QueuedMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &types1.MsgDelegate{} + v := &types.MsgDelegate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1910,7 +2034,7 @@ func (m *QueuedMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &types1.MsgUndelegate{} + v := &types.MsgUndelegate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1945,12 +2069,47 @@ func (m *QueuedMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &types1.MsgBeginRedelegate{} + v := &types.MsgBeginRedelegate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } m.Msg = &QueuedMessage_MsgBeginRedelegate{v} iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MsgCancelUnbondingDelegation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEpoching + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEpoching + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEpoching + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &types.MsgCancelUnbondingDelegation{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Msg = &QueuedMessage_MsgCancelUnbondingDelegation{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEpoching(dAtA[iNdEx:]) @@ -2396,6 +2555,42 @@ func (m *DelegationStateUpdate) Unmarshal(dAtA []byte) error { m.ValAddr = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEpoching + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEpoching + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEpoching + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Amount == nil { + m.Amount = &types1.Coin{} + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) } @@ -2414,7 +2609,7 @@ func (m *DelegationStateUpdate) Unmarshal(dAtA []byte) error { break } } - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BlockTime", wireType) } diff --git a/x/epoching/types/errors.go b/x/epoching/types/errors.go index bd1028ef3..1162299c1 100644 --- a/x/epoching/types/errors.go +++ b/x/epoching/types/errors.go @@ -8,9 +8,9 @@ import ( var ( ErrUnwrappedMsgType = errorsmod.Register(ModuleName, 1, ` - invalid message type in {MsgCreateValidator, MsgDelegate, MsgUndelegate, MsgBeginRedelegate} + invalid message type in {MsgCreateValidator, MsgDelegate, MsgUndelegate, MsgBeginRedelegate, MsgCancelUnbondingDelegation} messages. For creating a validator use the wrapped version under 'tx checkpointing create-validator' - and for the other messages use the wrapped versions under 'tx epoching {delegate,undelegate,redelegate}'`) + and for the other messages use the wrapped versions under 'tx epoching {delegate,undelegate,redelegate,cancel-unbond}'`) ErrInvalidQueuedMessageType = errorsmod.Register(ModuleName, 2, "invalid message type of a QueuedMessage") ErrUnknownEpochNumber = errorsmod.Register(ModuleName, 3, "the epoch number is not known in DB") ErrUnknownSlashedVotingPower = errorsmod.Register(ModuleName, 5, "the slashed voting power is not known in DB. Maybe the epoch has been checkpointed?") diff --git a/x/epoching/types/events.pb.go b/x/epoching/types/events.pb.go index 5037813b2..a1b01c203 100644 --- a/x/epoching/types/events.pb.go +++ b/x/epoching/types/events.pb.go @@ -505,6 +505,84 @@ func (m *EventWrappedBeginRedelegate) GetEpochBoundary() uint64 { return 0 } +// EventWrappedCancelUnbondingDelegation is the event emitted when a +// MsgWrappedCancelUnbondingDelegation has been queued +type EventWrappedCancelUnbondingDelegation struct { + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty"` + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` + Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + CreationHeight int64 `protobuf:"varint,4,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height,omitempty"` + EpochBoundary uint64 `protobuf:"varint,5,opt,name=epoch_boundary,json=epochBoundary,proto3" json:"epoch_boundary,omitempty"` +} + +func (m *EventWrappedCancelUnbondingDelegation) Reset() { *m = EventWrappedCancelUnbondingDelegation{} } +func (m *EventWrappedCancelUnbondingDelegation) String() string { return proto.CompactTextString(m) } +func (*EventWrappedCancelUnbondingDelegation) ProtoMessage() {} +func (*EventWrappedCancelUnbondingDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_2f0a2c43c7aaeb43, []int{7} +} +func (m *EventWrappedCancelUnbondingDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventWrappedCancelUnbondingDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventWrappedCancelUnbondingDelegation.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 *EventWrappedCancelUnbondingDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventWrappedCancelUnbondingDelegation.Merge(m, src) +} +func (m *EventWrappedCancelUnbondingDelegation) XXX_Size() int { + return m.Size() +} +func (m *EventWrappedCancelUnbondingDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_EventWrappedCancelUnbondingDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_EventWrappedCancelUnbondingDelegation proto.InternalMessageInfo + +func (m *EventWrappedCancelUnbondingDelegation) GetDelegatorAddress() string { + if m != nil { + return m.DelegatorAddress + } + return "" +} + +func (m *EventWrappedCancelUnbondingDelegation) GetValidatorAddress() string { + if m != nil { + return m.ValidatorAddress + } + return "" +} + +func (m *EventWrappedCancelUnbondingDelegation) GetAmount() uint64 { + if m != nil { + return m.Amount + } + return 0 +} + +func (m *EventWrappedCancelUnbondingDelegation) GetCreationHeight() int64 { + if m != nil { + return m.CreationHeight + } + return 0 +} + +func (m *EventWrappedCancelUnbondingDelegation) GetEpochBoundary() uint64 { + if m != nil { + return m.EpochBoundary + } + return 0 +} + func init() { proto.RegisterType((*EventBeginEpoch)(nil), "babylon.epoching.v1.EventBeginEpoch") proto.RegisterType((*EventEndEpoch)(nil), "babylon.epoching.v1.EventEndEpoch") @@ -513,51 +591,56 @@ func init() { proto.RegisterType((*EventWrappedDelegate)(nil), "babylon.epoching.v1.EventWrappedDelegate") proto.RegisterType((*EventWrappedUndelegate)(nil), "babylon.epoching.v1.EventWrappedUndelegate") proto.RegisterType((*EventWrappedBeginRedelegate)(nil), "babylon.epoching.v1.EventWrappedBeginRedelegate") + proto.RegisterType((*EventWrappedCancelUnbondingDelegation)(nil), "babylon.epoching.v1.EventWrappedCancelUnbondingDelegation") } func init() { proto.RegisterFile("babylon/epoching/v1/events.proto", fileDescriptor_2f0a2c43c7aaeb43) } var fileDescriptor_2f0a2c43c7aaeb43 = []byte{ - // 624 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x54, 0xdd, 0x6a, 0xd4, 0x4e, - 0x14, 0x6f, 0xf6, 0xeb, 0x4f, 0xe7, 0xdf, 0x6a, 0x3b, 0xbb, 0x2e, 0xc1, 0xe2, 0xba, 0x2e, 0x08, - 0x05, 0x75, 0xd3, 0xaa, 0x88, 0x78, 0xd7, 0xc5, 0x82, 0x15, 0x14, 0x8d, 0xb5, 0x82, 0x37, 0x61, - 0xb2, 0x73, 0x4c, 0x06, 0x93, 0x99, 0x30, 0x33, 0x59, 0xbb, 0x6f, 0xe1, 0x0b, 0x78, 0xe7, 0x03, - 0xf8, 0x12, 0xa2, 0x97, 0xbd, 0x14, 0x2f, 0x44, 0xda, 0x17, 0x91, 0x4c, 0x3e, 0x1a, 0x6d, 0x2b, - 0xe2, 0x9d, 0x77, 0x33, 0xbf, 0x8f, 0x33, 0xf9, 0x9d, 0x93, 0x19, 0x34, 0xf4, 0x89, 0x3f, 0x8f, - 0x04, 0x77, 0x20, 0x11, 0xd3, 0x90, 0xf1, 0xc0, 0x99, 0x6d, 0x3a, 0x30, 0x03, 0xae, 0xd5, 0x38, - 0x91, 0x42, 0x0b, 0xdc, 0x2d, 0x14, 0xe3, 0x52, 0x31, 0x9e, 0x6d, 0x5e, 0xec, 0x05, 0x22, 0x10, - 0x86, 0x77, 0xb2, 0x55, 0x2e, 0x1d, 0xdd, 0x46, 0xe7, 0xb7, 0x33, 0xeb, 0x04, 0x02, 0xc6, 0xb7, - 0x33, 0x39, 0xbe, 0x82, 0x96, 0x8c, 0xcf, 0xe3, 0x69, 0xec, 0x83, 0xb4, 0xad, 0xa1, 0xb5, 0xde, - 0x72, 0xff, 0x37, 0xd8, 0x63, 0x03, 0x8d, 0x6e, 0xa2, 0x65, 0xe3, 0xda, 0xe6, 0xf4, 0x8f, 0x3d, - 0x1f, 0x1a, 0xa8, 0x67, 0x4c, 0x0f, 0x08, 0xa7, 0x11, 0x3c, 0x4d, 0x21, 0x05, 0xfa, 0x48, 0x05, - 0x78, 0x8c, 0xba, 0x42, 0xb2, 0x80, 0x71, 0x12, 0x79, 0x26, 0x86, 0xa7, 0xe7, 0x09, 0x98, 0x12, - 0x8b, 0xee, 0x6a, 0x49, 0x19, 0xeb, 0xee, 0x3c, 0x81, 0x13, 0x67, 0x35, 0x4e, 0x9c, 0x85, 0xfb, - 0xa8, 0x13, 0x02, 0x0b, 0x42, 0x6d, 0x37, 0x0d, 0x59, 0xec, 0x70, 0x17, 0xb5, 0xf5, 0xbe, 0xc7, - 0xa8, 0xdd, 0x1a, 0x5a, 0xeb, 0x4b, 0x6e, 0x4b, 0xef, 0xef, 0x50, 0x7c, 0x01, 0x75, 0x62, 0x15, - 0x64, 0x68, 0xdb, 0xa0, 0xed, 0x58, 0x05, 0x3b, 0x14, 0xbf, 0xae, 0x7d, 0x16, 0xd1, 0x5a, 0x32, - 0x3f, 0xd5, 0xa0, 0xec, 0xce, 0xb0, 0xb9, 0xbe, 0x34, 0xb9, 0xf7, 0xf5, 0xdb, 0xe5, 0x3b, 0x01, - 0xd3, 0x61, 0xea, 0x8f, 0xa7, 0x22, 0x76, 0xa6, 0x22, 0x06, 0xed, 0xbf, 0xd2, 0xc7, 0x0b, 0xe2, - 0x4f, 0x99, 0x93, 0x05, 0x51, 0x63, 0xf3, 0xe9, 0x5b, 0x65, 0x09, 0x17, 0x97, 0x65, 0x2b, 0x48, - 0xe1, 0x1e, 0x6a, 0x83, 0x94, 0x42, 0xda, 0xff, 0x99, 0xd4, 0xf9, 0x66, 0xf4, 0xde, 0x42, 0x5d, - 0x63, 0x7e, 0x16, 0x11, 0x15, 0xee, 0x86, 0x12, 0x54, 0x28, 0x22, 0x8a, 0x37, 0x50, 0x4f, 0x65, - 0x08, 0x50, 0x6f, 0x26, 0x34, 0xe3, 0x81, 0x97, 0x88, 0x37, 0x45, 0xd7, 0x9b, 0x2e, 0x2e, 0xb8, - 0x3d, 0x43, 0x3d, 0xc9, 0x18, 0x7c, 0x1d, 0x61, 0x2d, 0x34, 0x89, 0x7e, 0xd6, 0x37, 0x8c, 0x7e, - 0xc5, 0x30, 0x75, 0xf5, 0x0d, 0x84, 0xab, 0xfa, 0x24, 0x62, 0x94, 0x68, 0x21, 0x95, 0xdd, 0xcc, - 0x92, 0xbb, 0xab, 0x65, 0xf5, 0x8a, 0x18, 0x7d, 0xb4, 0x8a, 0xc9, 0xbe, 0x90, 0x24, 0x49, 0x80, - 0xde, 0x87, 0x08, 0x02, 0xa2, 0x01, 0x5f, 0x43, 0xab, 0x34, 0x5f, 0x0b, 0xe9, 0x11, 0x4a, 0x25, - 0x28, 0x55, 0xcc, 0x75, 0xa5, 0x22, 0xb6, 0x72, 0x3c, 0x13, 0x57, 0x87, 0x55, 0xe2, 0x46, 0x2e, - 0xae, 0x88, 0x52, 0xdc, 0x47, 0x1d, 0x12, 0x8b, 0x94, 0x57, 0x03, 0xce, 0x77, 0x59, 0x1f, 0x29, - 0x70, 0x11, 0x9b, 0x01, 0x2f, 0xba, 0xf9, 0x06, 0x5f, 0x45, 0xe7, 0xf2, 0x3f, 0xc6, 0x17, 0x29, - 0xa7, 0x44, 0xce, 0xcd, 0xa4, 0x5b, 0xee, 0xb2, 0x41, 0x27, 0x05, 0x38, 0xfa, 0x64, 0xa1, 0x7e, - 0x3d, 0xc7, 0x73, 0x4e, 0xff, 0xd1, 0x24, 0xef, 0x1a, 0x68, 0xad, 0x9e, 0xc4, 0xdc, 0x6e, 0x17, - 0xfe, 0x2e, 0xce, 0x5d, 0x64, 0x2b, 0x91, 0xca, 0x29, 0x78, 0x67, 0xa5, 0xea, 0xe7, 0xfc, 0xde, - 0xaf, 0xd9, 0x26, 0xe8, 0x12, 0x05, 0xa5, 0x19, 0x27, 0x9a, 0x09, 0x7e, 0x8a, 0xbd, 0x69, 0xec, - 0x6b, 0x35, 0xd1, 0xde, 0xd9, 0xfd, 0x69, 0x9d, 0xde, 0x9f, 0xf6, 0xef, 0xfb, 0xd3, 0x39, 0xa5, - 0x3f, 0x93, 0x87, 0x9f, 0x0f, 0x07, 0xd6, 0xc1, 0xe1, 0xc0, 0xfa, 0x7e, 0x38, 0xb0, 0xde, 0x1e, - 0x0d, 0x16, 0x0e, 0x8e, 0x06, 0x0b, 0x5f, 0x8e, 0x06, 0x0b, 0x2f, 0x37, 0x6a, 0x97, 0xba, 0x78, - 0x45, 0xa7, 0x21, 0x61, 0xbc, 0xdc, 0x38, 0xfb, 0xc7, 0xcf, 0xae, 0xb9, 0xdd, 0x7e, 0xc7, 0x3c, - 0xa4, 0xb7, 0x7e, 0x04, 0x00, 0x00, 0xff, 0xff, 0x77, 0x37, 0xe9, 0x99, 0x97, 0x05, 0x00, 0x00, + // 674 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x55, 0xcd, 0x6e, 0xd4, 0x3c, + 0x14, 0x6d, 0xe6, 0xef, 0x53, 0xfd, 0xf5, 0xd7, 0x33, 0x8c, 0x22, 0x2a, 0x86, 0x61, 0xa4, 0x8a, + 0x4a, 0xc0, 0xa4, 0x05, 0x84, 0x10, 0xbb, 0x0e, 0x54, 0x6a, 0x91, 0x40, 0x10, 0xda, 0x22, 0xb1, + 0x89, 0x9c, 0xd8, 0x24, 0x16, 0x89, 0x1d, 0xd9, 0xce, 0xd0, 0x79, 0x0b, 0x5e, 0x80, 0x1d, 0x0f, + 0xc0, 0x4b, 0x20, 0x58, 0x76, 0x89, 0x58, 0x20, 0xd4, 0xae, 0x78, 0x0b, 0x14, 0xe7, 0xa7, 0x81, + 0x4e, 0x51, 0xc5, 0x06, 0xb1, 0xf3, 0x3d, 0xe7, 0xdc, 0xeb, 0x9c, 0x7b, 0x1d, 0x1b, 0xf4, 0x5d, + 0xe4, 0x4e, 0x42, 0xce, 0x2c, 0x12, 0x73, 0x2f, 0xa0, 0xcc, 0xb7, 0xc6, 0x1b, 0x16, 0x19, 0x13, + 0xa6, 0xe4, 0x30, 0x16, 0x5c, 0x71, 0xd8, 0xce, 0x15, 0xc3, 0x42, 0x31, 0x1c, 0x6f, 0x5c, 0xec, + 0xf8, 0xdc, 0xe7, 0x9a, 0xb7, 0xd2, 0x55, 0x26, 0x1d, 0xdc, 0x06, 0x8b, 0x5b, 0x69, 0xea, 0x88, + 0xf8, 0x94, 0x6d, 0xa5, 0x72, 0x78, 0x05, 0xcc, 0xe9, 0x3c, 0x87, 0x25, 0x91, 0x4b, 0x84, 0x69, + 0xf4, 0x8d, 0xb5, 0x86, 0xfd, 0xbf, 0xc6, 0x1e, 0x6b, 0x68, 0x70, 0x13, 0xcc, 0xeb, 0xac, 0x2d, + 0x86, 0xcf, 0x9d, 0xf3, 0xbe, 0x06, 0x3a, 0x3a, 0x69, 0x1b, 0x31, 0x1c, 0x92, 0xa7, 0x09, 0x49, + 0x08, 0x7e, 0x24, 0x7d, 0x38, 0x04, 0x6d, 0x2e, 0xa8, 0x4f, 0x19, 0x0a, 0x1d, 0x6d, 0xc3, 0x51, + 0x93, 0x98, 0xe8, 0x12, 0xb3, 0xf6, 0x72, 0x41, 0xe9, 0xd4, 0xdd, 0x49, 0x4c, 0x4e, 0xed, 0x55, + 0x3b, 0xb5, 0x17, 0xec, 0x82, 0x56, 0x40, 0xa8, 0x1f, 0x28, 0xb3, 0xae, 0xc9, 0x3c, 0x82, 0x6d, + 0xd0, 0x54, 0x07, 0x0e, 0xc5, 0x66, 0xa3, 0x6f, 0xac, 0xcd, 0xd9, 0x0d, 0x75, 0xb0, 0x83, 0xe1, + 0x05, 0xd0, 0x8a, 0xa4, 0x9f, 0xa2, 0x4d, 0x8d, 0x36, 0x23, 0xe9, 0xef, 0x60, 0xf8, 0xaa, 0xf2, + 0x59, 0x48, 0x29, 0x41, 0xdd, 0x44, 0x11, 0x69, 0xb6, 0xfa, 0xf5, 0xb5, 0xb9, 0xd1, 0xbd, 0x2f, + 0x5f, 0x2f, 0xdf, 0xf1, 0xa9, 0x0a, 0x12, 0x77, 0xe8, 0xf1, 0xc8, 0xf2, 0x78, 0x44, 0x94, 0xfb, + 0x52, 0x9d, 0x2c, 0x90, 0xeb, 0x51, 0x2b, 0x35, 0x22, 0x87, 0xfa, 0xd3, 0x37, 0x8b, 0x12, 0x36, + 0x2c, 0xca, 0x96, 0x90, 0x84, 0x1d, 0xd0, 0x24, 0x42, 0x70, 0x61, 0xfe, 0xa7, 0x5d, 0x67, 0xc1, + 0xe0, 0x9d, 0x01, 0xda, 0x3a, 0xf9, 0x59, 0x88, 0x64, 0xb0, 0x1b, 0x08, 0x22, 0x03, 0x1e, 0x62, + 0xb8, 0x0e, 0x3a, 0x32, 0x45, 0x08, 0x76, 0xc6, 0x5c, 0x51, 0xe6, 0x3b, 0x31, 0x7f, 0x9d, 0x77, + 0xbd, 0x6e, 0xc3, 0x9c, 0xdb, 0xd7, 0xd4, 0x93, 0x94, 0x81, 0xd7, 0x01, 0x54, 0x5c, 0xa1, 0xf0, + 0x67, 0x7d, 0x4d, 0xeb, 0x97, 0x34, 0x53, 0x55, 0xdf, 0x00, 0xb0, 0xac, 0x8f, 0x42, 0x8a, 0x91, + 0xe2, 0x42, 0x9a, 0xf5, 0xd4, 0xb9, 0xbd, 0x5c, 0x54, 0x2f, 0x89, 0xc1, 0x07, 0x23, 0x9f, 0xec, + 0x73, 0x81, 0xe2, 0x98, 0xe0, 0x07, 0x24, 0x24, 0x3e, 0x52, 0x04, 0x5e, 0x03, 0xcb, 0x38, 0x5b, + 0x73, 0xe1, 0x20, 0x8c, 0x05, 0x91, 0x32, 0x9f, 0xeb, 0x52, 0x49, 0x6c, 0x66, 0x78, 0x2a, 0x2e, + 0x37, 0x2b, 0xc5, 0xb5, 0x4c, 0x5c, 0x12, 0x85, 0xb8, 0x0b, 0x5a, 0x28, 0xe2, 0x09, 0x2b, 0x07, + 0x9c, 0x45, 0x69, 0x1f, 0x31, 0x61, 0x3c, 0xd2, 0x03, 0x9e, 0xb5, 0xb3, 0x00, 0xae, 0x82, 0x85, + 0xec, 0xc4, 0xb8, 0x3c, 0x61, 0x18, 0x89, 0x89, 0x9e, 0x74, 0xc3, 0x9e, 0xd7, 0xe8, 0x28, 0x07, + 0x07, 0x1f, 0x0d, 0xd0, 0xad, 0xfa, 0xd8, 0x63, 0xf8, 0x1f, 0x75, 0xf2, 0xb6, 0x06, 0x56, 0xaa, + 0x4e, 0xf4, 0xdf, 0x6d, 0x93, 0x3f, 0xb3, 0x73, 0x17, 0x98, 0x92, 0x27, 0xc2, 0x23, 0xce, 0x59, + 0xae, 0xba, 0x19, 0xbf, 0xff, 0xab, 0xb7, 0x11, 0xb8, 0x84, 0x89, 0x54, 0x94, 0x21, 0x45, 0x39, + 0x9b, 0x92, 0x5e, 0xd7, 0xe9, 0x2b, 0x15, 0xd1, 0xfe, 0xd9, 0xfd, 0x69, 0x4c, 0xef, 0x4f, 0xf3, + 0xf7, 0xfd, 0x69, 0x4d, 0xeb, 0xcf, 0x77, 0x03, 0xac, 0x56, 0xfb, 0x73, 0x1f, 0x31, 0x8f, 0x84, + 0x7b, 0xcc, 0xe5, 0x0c, 0x53, 0xe6, 0xe7, 0x07, 0x98, 0x72, 0xf6, 0x17, 0x06, 0x7f, 0x15, 0x2c, + 0x7a, 0x82, 0x64, 0x1d, 0xcb, 0x2f, 0xb1, 0x86, 0xfe, 0x4f, 0x17, 0x0a, 0x78, 0x3b, 0xbb, 0xcc, + 0xce, 0x77, 0x16, 0x46, 0x0f, 0x3f, 0x1d, 0xf5, 0x8c, 0xc3, 0xa3, 0x9e, 0xf1, 0xed, 0xa8, 0x67, + 0xbc, 0x39, 0xee, 0xcd, 0x1c, 0x1e, 0xf7, 0x66, 0x3e, 0x1f, 0xf7, 0x66, 0x5e, 0xac, 0x57, 0x2e, + 0xb0, 0xfc, 0xc5, 0xf0, 0x02, 0x44, 0x59, 0x11, 0x58, 0x07, 0x27, 0x4f, 0x8c, 0xbe, 0xc9, 0xdc, + 0x96, 0x7e, 0x34, 0x6e, 0xfd, 0x08, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x83, 0x9b, 0xc9, 0x83, 0x06, + 0x00, 0x00, } func (m *EventBeginEpoch) Marshal() (dAtA []byte, err error) { @@ -902,6 +985,58 @@ func (m *EventWrappedBeginRedelegate) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *EventWrappedCancelUnbondingDelegation) 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 *EventWrappedCancelUnbondingDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventWrappedCancelUnbondingDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EpochBoundary != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.EpochBoundary)) + i-- + dAtA[i] = 0x28 + } + if m.CreationHeight != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.CreationHeight)) + i-- + dAtA[i] = 0x20 + } + if m.Amount != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Amount)) + i-- + dAtA[i] = 0x18 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -1080,6 +1215,32 @@ func (m *EventWrappedBeginRedelegate) Size() (n int) { return n } +func (m *EventWrappedCancelUnbondingDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Amount != 0 { + n += 1 + sovEvents(uint64(m.Amount)) + } + if m.CreationHeight != 0 { + n += 1 + sovEvents(uint64(m.CreationHeight)) + } + if m.EpochBoundary != 0 { + n += 1 + sovEvents(uint64(m.EpochBoundary)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2183,6 +2344,177 @@ func (m *EventWrappedBeginRedelegate) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventWrappedCancelUnbondingDelegation) 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 ErrIntOverflowEvents + } + 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: EventWrappedCancelUnbondingDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventWrappedCancelUnbondingDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + m.Amount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Amount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationHeight", wireType) + } + m.CreationHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreationHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochBoundary", wireType) + } + m.EpochBoundary = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EpochBoundary |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/epoching/types/expected_keepers.go b/x/epoching/types/expected_keepers.go index 17e23b9ab..3cbc29698 100644 --- a/x/epoching/types/expected_keepers.go +++ b/x/epoching/types/expected_keepers.go @@ -1,51 +1,52 @@ package types import ( + "context" "time" + storetypes "cosmossdk.io/store/types" + cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "cosmossdk.io/math" abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AccountKeeper defines the expected account keeper used for simulations (noalias) type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI // Methods imported from account should be defined here } // BankKeeper defines the expected interface needed to retrieve account balances. type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin + SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins + GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin // Methods imported from bank should be defined here } // StakingKeeper defines the staking module interface contract needed by the // epoching module. type StakingKeeper interface { - GetParams(ctx sdk.Context) stakingtypes.Params - DequeueAllMatureUBDQueue(ctx sdk.Context, currTime time.Time) (matureUnbonds []stakingtypes.DVPair) - CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) - DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []stakingtypes.DVVTriplet) - CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (sdk.Coins, error) - ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) ([]abci.ValidatorUpdate, error) - IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power int64) bool) - GetValidator(ctx sdk.Context, addr sdk.ValAddress) (stakingtypes.Validator, bool) - GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) []stakingtypes.Delegation - HasMaxUnbondingDelegationEntries(ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool - BondDenom(ctx sdk.Context) string - HasReceivingRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool - HasMaxRedelegationEntries(ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorSrcAddr, validatorDstAddr sdk.ValAddress) bool - ValidateUnbondAmount(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt math.Int) (sdk.Dec, error) - ValidatorQueueIterator(ctx sdk.Context, endTime time.Time, endHeight int64) sdk.Iterator - UnbondingToUnbonded(ctx sdk.Context, validator stakingtypes.Validator) stakingtypes.Validator - RemoveValidator(ctx sdk.Context, address sdk.ValAddress) - UnbondAllMatureValidators(ctx sdk.Context) - GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (stakingtypes.Validator, bool) + GetParams(ctx context.Context) (stakingtypes.Params, error) + DequeueAllMatureUBDQueue(ctx context.Context, currTime time.Time) ([]stakingtypes.DVPair, error) + CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) + DequeueAllMatureRedelegationQueue(ctx context.Context, currTime time.Time) ([]stakingtypes.DVVTriplet, error) + CompleteRedelegation(ctx context.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (sdk.Coins, error) + ApplyAndReturnValidatorSetUpdates(ctx context.Context) ([]abci.ValidatorUpdate, error) + IterateLastValidatorPowers(ctx context.Context, handler func(operator sdk.ValAddress, power int64) bool) error + GetValidator(ctx context.Context, addr sdk.ValAddress) (stakingtypes.Validator, error) + GetValidatorDelegations(ctx context.Context, valAddr sdk.ValAddress) ([]stakingtypes.Delegation, error) + HasMaxUnbondingDelegationEntries(ctx context.Context, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) (bool, error) + BondDenom(ctx context.Context) (string, error) + HasReceivingRedelegation(ctx context.Context, delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) (bool, error) + HasMaxRedelegationEntries(ctx context.Context, delegatorAddr sdk.AccAddress, validatorSrcAddr, validatorDstAddr sdk.ValAddress) (bool, error) + ValidateUnbondAmount(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt math.Int) (math.LegacyDec, error) + ValidatorQueueIterator(ctx context.Context, endTime time.Time, endHeight int64) (storetypes.Iterator, error) + UnbondAllMatureValidators(ctx context.Context) error + GetValidatorByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (stakingtypes.Validator, error) + GetPubKeyByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (cmtprotocrypto.PublicKey, error) } // Event Hooks @@ -56,14 +57,14 @@ type StakingKeeper interface { // EpochingHooks event hooks for epoching validator object (noalias) type EpochingHooks interface { - AfterEpochBegins(ctx sdk.Context, epoch uint64) // Must be called after an epoch begins - AfterEpochEnds(ctx sdk.Context, epoch uint64) // Must be called after an epoch ends - BeforeSlashThreshold(ctx sdk.Context, valSet ValidatorSet) // Must be called before a certain threshold (1/3 or 2/3) of validators are slashed in a single epoch + AfterEpochBegins(ctx context.Context, epoch uint64) // Must be called after an epoch begins + AfterEpochEnds(ctx context.Context, epoch uint64) // Must be called after an epoch ends + BeforeSlashThreshold(ctx context.Context, valSet ValidatorSet) // Must be called before a certain threshold (1/3 or 2/3) of validators are slashed in a single epoch } // StakingHooks event hooks for staking validator object (noalias) type StakingHooks interface { - BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) // Must be called right before a validator is slashed + BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction math.LegacyDec) // Must be called right before a validator is slashed AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error // Must be called when a validator is created AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error // Must be called when a validator is deleted AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error // Must be called when a validator is bonded diff --git a/x/epoching/types/genesis_test.go b/x/epoching/types/genesis_test.go index 59038f82c..925ae1db6 100644 --- a/x/epoching/types/genesis_test.go +++ b/x/epoching/types/genesis_test.go @@ -7,7 +7,6 @@ import ( "github.com/babylonchain/babylon/testutil/nullify" "github.com/babylonchain/babylon/x/epoching" "github.com/babylonchain/babylon/x/epoching/types" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/stretchr/testify/require" ) @@ -15,7 +14,7 @@ func TestGenesis(t *testing.T) { // This test requires setting up the staking module // Otherwise the epoching module cannot initialise the genesis validator set app := app.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) keeper := app.EpochingKeeper genesisState := types.GenesisState{ diff --git a/x/epoching/types/hooks.go b/x/epoching/types/hooks.go index 9a220da68..ce921259e 100644 --- a/x/epoching/types/hooks.go +++ b/x/epoching/types/hooks.go @@ -1,7 +1,7 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "context" ) // combine multiple Epoching hooks, all hook functions are run in array sequence @@ -13,19 +13,19 @@ func NewMultiEpochingHooks(hooks ...EpochingHooks) MultiEpochingHooks { return hooks } -func (h MultiEpochingHooks) AfterEpochBegins(ctx sdk.Context, epoch uint64) { +func (h MultiEpochingHooks) AfterEpochBegins(ctx context.Context, epoch uint64) { for i := range h { h[i].AfterEpochBegins(ctx, epoch) } } -func (h MultiEpochingHooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) { +func (h MultiEpochingHooks) AfterEpochEnds(ctx context.Context, epoch uint64) { for i := range h { h[i].AfterEpochEnds(ctx, epoch) } } -func (h MultiEpochingHooks) BeforeSlashThreshold(ctx sdk.Context, valSet ValidatorSet) { +func (h MultiEpochingHooks) BeforeSlashThreshold(ctx context.Context, valSet ValidatorSet) { for i := range h { h[i].BeforeSlashThreshold(ctx, valSet) } diff --git a/x/epoching/types/keys.go b/x/epoching/types/keys.go index 417a3b433..5ec668b83 100644 --- a/x/epoching/types/keys.go +++ b/x/epoching/types/keys.go @@ -18,18 +18,17 @@ const ( ) var ( - EpochNumberKey = []byte{0x11} // key prefix for the epoch number - EpochInfoKey = []byte{0x12} // key prefix for the epoch info - QueueLengthKey = []byte{0x13} // key prefix for the queue length - MsgQueueKey = []byte{0x14} // key prefix for the message queue of an epoch - ValidatorSetKey = []byte{0x15} // key prefix for the validator set in a single epoch - VotingPowerKey = []byte{0x16} // key prefix for the total voting power of a validator set in a single epoch - SlashedVotingPowerKey = []byte{0x17} // key prefix for the total slashed voting power in a single epoch - SlashedValidatorSetKey = []byte{0x18} // key prefix for slashed validator set - ValidatorLifecycleKey = []byte{0x19} // key prefix for validator life cycle - DelegationLifecycleKey = []byte{0x20} // key prefix for delegation life cycle - AppHashKey = []byte{0x21} // key prefix for the app hash - ParamsKey = []byte{0x22} // key prefix for the parameters + EpochInfoKey = []byte{0x11} // key prefix for the epoch info + QueueLengthKey = []byte{0x12} // key prefix for the queue length + MsgQueueKey = []byte{0x13} // key prefix for the message queue of an epoch + ValidatorSetKey = []byte{0x14} // key prefix for the validator set in a single epoch + VotingPowerKey = []byte{0x15} // key prefix for the total voting power of a validator set in a single epoch + SlashedVotingPowerKey = []byte{0x16} // key prefix for the total slashed voting power in a single epoch + SlashedValidatorSetKey = []byte{0x17} // key prefix for slashed validator set + ValidatorLifecycleKey = []byte{0x18} // key prefix for validator life cycle + DelegationLifecycleKey = []byte{0x19} // key prefix for delegation life cycle + AppHashKey = []byte{0x20} // key prefix for the app hash + ParamsKey = []byte{0x21} // key prefix for the parameters ) func KeyPrefix(p string) []byte { diff --git a/x/epoching/types/msg.go b/x/epoching/types/msg.go index bac4f93b5..97f73d92d 100644 --- a/x/epoching/types/msg.go +++ b/x/epoching/types/msg.go @@ -1,23 +1,16 @@ package types import ( - errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// staking message types -const ( - TypeMsgWrappedDelegate = "wrapped_delegate" - TypeMsgWrappedUndelegate = "wrapped_begin_unbonding" - TypeMsgWrappedBeginRedelegate = "wrapped_begin_redelegate" -) - // ensure that these message types implement the sdk.Msg interface var ( _ sdk.Msg = &MsgWrappedDelegate{} _ sdk.Msg = &MsgWrappedUndelegate{} _ sdk.Msg = &MsgWrappedBeginRedelegate{} + _ sdk.Msg = &MsgWrappedCancelUnbondingDelegation{} _ sdk.Msg = &MsgUpdateParams{} ) @@ -28,33 +21,6 @@ func NewMsgWrappedDelegate(msg *stakingtypes.MsgDelegate) *MsgWrappedDelegate { } } -// Route implements the sdk.Msg interface. -func (msg MsgWrappedDelegate) Route() string { return RouterKey } - -// Type implements the sdk.Msg interface. -func (msg MsgWrappedDelegate) Type() string { return TypeMsgWrappedDelegate } - -// GetSigners implements the sdk.Msg interface. It returns the address(es) that -// must sign over msg.GetSignBytes(). -// If the validator address is not same as delegator's, then the validator must -// sign the msg as well. -func (msg MsgWrappedDelegate) GetSigners() []sdk.AccAddress { - return msg.Msg.GetSigners() -} - -// GetSignBytes returns the message bytes to sign over. -func (msg MsgWrappedDelegate) GetSignBytes() []byte { - return msg.Msg.GetSignBytes() -} - -// ValidateBasic implements the sdk.Msg interface. -func (msg MsgWrappedDelegate) ValidateBasic() error { - if msg.Msg == nil { - return ErrNoWrappedMsg - } - return msg.Msg.ValidateBasic() -} - // NewMsgWrappedUndelegate creates a new MsgWrappedUndelegate instance. func NewMsgWrappedUndelegate(msg *stakingtypes.MsgUndelegate) *MsgWrappedUndelegate { return &MsgWrappedUndelegate{ @@ -62,33 +28,6 @@ func NewMsgWrappedUndelegate(msg *stakingtypes.MsgUndelegate) *MsgWrappedUndeleg } } -// Route implements the sdk.Msg interface. -func (msg MsgWrappedUndelegate) Route() string { return RouterKey } - -// Type implements the sdk.Msg interface. -func (msg MsgWrappedUndelegate) Type() string { return TypeMsgWrappedUndelegate } - -// GetSigners implements the sdk.Msg interface. It returns the address(es) that -// must sign over msg.GetSignBytes(). -// If the validator address is not same as delegator's, then the validator must -// sign the msg as well. -func (msg MsgWrappedUndelegate) GetSigners() []sdk.AccAddress { - return msg.Msg.GetSigners() -} - -// GetSignBytes returns the message bytes to sign over. -func (msg MsgWrappedUndelegate) GetSignBytes() []byte { - return msg.Msg.GetSignBytes() -} - -// ValidateBasic implements the sdk.Msg interface. -func (msg MsgWrappedUndelegate) ValidateBasic() error { - if msg.Msg == nil { - return ErrNoWrappedMsg - } - return msg.Msg.ValidateBasic() -} - // NewMsgWrappedBeginRedelegate creates a new MsgWrappedBeginRedelegate instance. func NewMsgWrappedBeginRedelegate(msg *stakingtypes.MsgBeginRedelegate) *MsgWrappedBeginRedelegate { return &MsgWrappedBeginRedelegate{ @@ -96,48 +35,9 @@ func NewMsgWrappedBeginRedelegate(msg *stakingtypes.MsgBeginRedelegate) *MsgWrap } } -// Route implements the sdk.Msg interface. -func (msg MsgWrappedBeginRedelegate) Route() string { return RouterKey } - -// Type implements the sdk.Msg interface. -func (msg MsgWrappedBeginRedelegate) Type() string { return TypeMsgWrappedBeginRedelegate } - -// GetSigners implements the sdk.Msg interface. It returns the address(es) that -// must sign over msg.GetSignBytes(). -// If the validator address is not same as delegator's, then the validator must -// sign the msg as well. -func (msg MsgWrappedBeginRedelegate) GetSigners() []sdk.AccAddress { - return msg.Msg.GetSigners() -} - -// GetSignBytes returns the message bytes to sign over. -func (msg MsgWrappedBeginRedelegate) GetSignBytes() []byte { - return msg.Msg.GetSignBytes() -} - -// ValidateBasic implements the sdk.Msg interface. -func (msg MsgWrappedBeginRedelegate) ValidateBasic() error { - if msg.Msg == nil { - return ErrNoWrappedMsg - } - return msg.Msg.ValidateBasic() -} - -// 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 +// NewMsgWrappedCancelUnbondingDelegation creates a new MsgWrappedCancelUnbondingDelegation instance. +func NewMsgWrappedCancelUnbondingDelegation(msg *stakingtypes.MsgCancelUnbondingDelegation) *MsgWrappedCancelUnbondingDelegation { + return &MsgWrappedCancelUnbondingDelegation{ + Msg: msg, } - - return nil } diff --git a/x/epoching/types/msg_test.go b/x/epoching/types/msg_test.go index 6e1f3ca07..f5822d564 100644 --- a/x/epoching/types/msg_test.go +++ b/x/epoching/types/msg_test.go @@ -1,10 +1,12 @@ package types_test import ( - appparams "github.com/babylonchain/babylon/app/params" "testing" "time" + sdkmath "cosmossdk.io/math" + appparams "github.com/babylonchain/babylon/app/params" + "github.com/stretchr/testify/require" "github.com/babylonchain/babylon/x/epoching/types" @@ -22,15 +24,10 @@ import ( var ( pk1 = ed25519.GenPrivKey().PubKey() pk2 = ed25519.GenPrivKey().PubKey() - pk3 = ed25519.GenPrivKey().PubKey() valAddr1 = sdk.ValAddress(pk1.Address()) valAddr2 = sdk.ValAddress(pk2.Address()) - valAddr3 = sdk.ValAddress(pk3.Address()) - - emptyAddr sdk.ValAddress - coinPos = sdk.NewInt64Coin(appparams.DefaultBondDenom, 1000) - coinZero = sdk.NewInt64Coin(appparams.DefaultBondDenom, 0) + coinPos = sdk.NewInt64Coin(appparams.DefaultBondDenom, 1000) ) func TestMsgDecode(t *testing.T) { @@ -49,7 +46,7 @@ func TestMsgDecode(t *testing.T) { require.True(t, pk1.Equals(pkUnmarshaled.(*ed25519.PubKey))) // create unwrapped msg - msgUnwrapped := stakingtypes.NewMsgDelegate(sdk.AccAddress(valAddr1), valAddr2, coinPos) + msgUnwrapped := stakingtypes.NewMsgDelegate(sdk.AccAddress(valAddr1).String(), valAddr2.String(), coinPos) // wrap and marshal msg msg := types.NewMsgWrappedDelegate(msgUnwrapped) @@ -68,8 +65,8 @@ func TestMsgDecode(t *testing.T) { var qmsgUnmarshaled sdk.Msg var msgCreateValUnmarshaled sdk.Msg - commission1 := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - msgcreateval1, err := stakingtypes.NewMsgCreateValidator(valAddr1, pk1, coinPos, stakingtypes.Description{}, commission1, sdk.OneInt()) + commission1 := stakingtypes.NewCommissionRates(sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec()) + msgcreateval1, err := stakingtypes.NewMsgCreateValidator(valAddr1.String(), pk1, coinPos, stakingtypes.Description{}, commission1, sdkmath.OneInt()) require.NoError(t, err) qmsg, err := types.NewQueuedMessage(1, time.Now(), []byte("tx id 1"), msgcreateval1) require.NoError(t, err) @@ -90,105 +87,3 @@ func TestMsgDecode(t *testing.T) { require.Equal(t, qmsg.MsgId, qmsg2.MsgId) require.True(t, msgcreateval1.Pubkey.Equal(msgcreateval2.Pubkey)) } - -// test ValidateBasic for MsgWrappedDelegate -func TestMsgWrappedDelegate(t *testing.T) { - tests := []struct { - name string - delegatorAddr sdk.AccAddress - validatorAddr sdk.ValAddress - bond sdk.Coin - expectPass bool - }{ - {"basic good", sdk.AccAddress(valAddr1), valAddr2, coinPos, true}, - {"no wrapped msg", nil, nil, coinPos, false}, - {"self bond", sdk.AccAddress(valAddr1), valAddr1, coinPos, true}, - {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, coinPos, false}, - {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, coinPos, false}, - {"empty bond", sdk.AccAddress(valAddr1), valAddr2, coinZero, false}, - {"nil bold", sdk.AccAddress(valAddr1), valAddr2, sdk.Coin{}, false}, - } - - for _, tc := range tests { - var msg *types.MsgWrappedDelegate - if tc.delegatorAddr == nil { - msg = types.NewMsgWrappedDelegate(nil) - } else { - msgUnwrapped := stakingtypes.NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond) - msg = types.NewMsgWrappedDelegate(msgUnwrapped) - } - if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", tc.name) - } else { - require.Error(t, msg.ValidateBasic(), "test: %v", tc.name) - } - } -} - -// test ValidateBasic for MsgWrappedBeginRedelegate -func TestMsgWrappedBeginRedelegate(t *testing.T) { - tests := []struct { - name string - delegatorAddr sdk.AccAddress - validatorSrcAddr sdk.ValAddress - validatorDstAddr sdk.ValAddress - amount sdk.Coin - expectPass bool - }{ - {"regular", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), true}, - {"no wrapped msg", nil, nil, nil, coinPos, false}, - {"zero amount", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(appparams.DefaultBondDenom, 0), false}, - {"nil amount", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.Coin{}, false}, - {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, valAddr3, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), false}, - {"empty source validator", sdk.AccAddress(valAddr1), emptyAddr, valAddr3, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), false}, - {"empty destination validator", sdk.AccAddress(valAddr1), valAddr2, emptyAddr, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), false}, - } - - for _, tc := range tests { - var msg *types.MsgWrappedBeginRedelegate - if tc.delegatorAddr == nil { - msg = types.NewMsgWrappedBeginRedelegate(nil) - } else { - msgUnwrapped := stakingtypes.NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.amount) - msg = types.NewMsgWrappedBeginRedelegate(msgUnwrapped) - } - if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", tc.name) - } else { - require.Error(t, msg.ValidateBasic(), "test: %v", tc.name) - } - } -} - -// test ValidateBasic for MsgWrappedUndelegate -func TestMsgWrappedUndelegate(t *testing.T) { - tests := []struct { - name string - delegatorAddr sdk.AccAddress - validatorAddr sdk.ValAddress - amount sdk.Coin - expectPass bool - }{ - {"regular", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), true}, - {"no wrapped msg", nil, nil, coinPos, false}, - {"zero amount", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(appparams.DefaultBondDenom, 0), false}, - {"nil amount", sdk.AccAddress(valAddr1), valAddr2, sdk.Coin{}, false}, - {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), false}, - {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, sdk.NewInt64Coin(appparams.DefaultBondDenom, 1), false}, - } - - for _, tc := range tests { - var msg *types.MsgWrappedUndelegate - if tc.delegatorAddr == nil { - msg = types.NewMsgWrappedUndelegate(nil) - } else { - msgUnwrapped := stakingtypes.NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.amount) - msg = types.NewMsgWrappedUndelegate(msgUnwrapped) - } - if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", tc.name) - } else { - require.Error(t, msg.ValidateBasic(), "test: %v", tc.name) - } - } -} diff --git a/x/epoching/types/params.go b/x/epoching/types/params.go index 1b14269ed..e03a402fd 100644 --- a/x/epoching/types/params.go +++ b/x/epoching/types/params.go @@ -1,17 +1,13 @@ package types import ( - fmt "fmt" + "fmt" ) const ( DefaultEpochInterval uint64 = 10 ) -var ( - KeyEpochInterval = []byte("EpochInterval") -) - // NewParams creates a new Params instance func NewParams(epochInterval uint64) Params { return Params{ diff --git a/x/epoching/types/tx.pb.go b/x/epoching/types/tx.pb.go index 56c8ce0b9..fd16d4af4 100644 --- a/x/epoching/types/tx.pb.go +++ b/x/epoching/types/tx.pb.go @@ -259,6 +259,87 @@ func (m *MsgWrappedBeginRedelegateResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgWrappedBeginRedelegateResponse proto.InternalMessageInfo +// MsgWrappedCancelUnbondingDelegation is the message for cancelling +// an unbonding delegation +type MsgWrappedCancelUnbondingDelegation struct { + Msg *types.MsgCancelUnbondingDelegation `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (m *MsgWrappedCancelUnbondingDelegation) Reset() { *m = MsgWrappedCancelUnbondingDelegation{} } +func (m *MsgWrappedCancelUnbondingDelegation) String() string { return proto.CompactTextString(m) } +func (*MsgWrappedCancelUnbondingDelegation) ProtoMessage() {} +func (*MsgWrappedCancelUnbondingDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_a5fc8fed8f4e58b6, []int{6} +} +func (m *MsgWrappedCancelUnbondingDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWrappedCancelUnbondingDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWrappedCancelUnbondingDelegation.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 *MsgWrappedCancelUnbondingDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWrappedCancelUnbondingDelegation.Merge(m, src) +} +func (m *MsgWrappedCancelUnbondingDelegation) XXX_Size() int { + return m.Size() +} +func (m *MsgWrappedCancelUnbondingDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWrappedCancelUnbondingDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWrappedCancelUnbondingDelegation proto.InternalMessageInfo + +// MsgWrappedCancelUnbondingDelegationResponse is the response to the +// MsgWrappedCancelUnbondingDelegation message +type MsgWrappedCancelUnbondingDelegationResponse struct { +} + +func (m *MsgWrappedCancelUnbondingDelegationResponse) Reset() { + *m = MsgWrappedCancelUnbondingDelegationResponse{} +} +func (m *MsgWrappedCancelUnbondingDelegationResponse) String() string { + return proto.CompactTextString(m) +} +func (*MsgWrappedCancelUnbondingDelegationResponse) ProtoMessage() {} +func (*MsgWrappedCancelUnbondingDelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a5fc8fed8f4e58b6, []int{7} +} +func (m *MsgWrappedCancelUnbondingDelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWrappedCancelUnbondingDelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWrappedCancelUnbondingDelegationResponse.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 *MsgWrappedCancelUnbondingDelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWrappedCancelUnbondingDelegationResponse.Merge(m, src) +} +func (m *MsgWrappedCancelUnbondingDelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWrappedCancelUnbondingDelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWrappedCancelUnbondingDelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWrappedCancelUnbondingDelegationResponse proto.InternalMessageInfo + // MsgUpdateParams defines a message for updating epoching module parameters. type MsgUpdateParams struct { // authority is the address of the governance account. @@ -266,7 +347,7 @@ type MsgUpdateParams struct { // 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. + // params defines the epoching parameters to update. // // NOTE: All parameters must be supplied. Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` @@ -276,7 +357,7 @@ 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_a5fc8fed8f4e58b6, []int{6} + return fileDescriptor_a5fc8fed8f4e58b6, []int{8} } func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -327,7 +408,7 @@ 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_a5fc8fed8f4e58b6, []int{7} + return fileDescriptor_a5fc8fed8f4e58b6, []int{9} } func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -363,6 +444,8 @@ func init() { proto.RegisterType((*MsgWrappedUndelegateResponse)(nil), "babylon.epoching.v1.MsgWrappedUndelegateResponse") proto.RegisterType((*MsgWrappedBeginRedelegate)(nil), "babylon.epoching.v1.MsgWrappedBeginRedelegate") proto.RegisterType((*MsgWrappedBeginRedelegateResponse)(nil), "babylon.epoching.v1.MsgWrappedBeginRedelegateResponse") + proto.RegisterType((*MsgWrappedCancelUnbondingDelegation)(nil), "babylon.epoching.v1.MsgWrappedCancelUnbondingDelegation") + proto.RegisterType((*MsgWrappedCancelUnbondingDelegationResponse)(nil), "babylon.epoching.v1.MsgWrappedCancelUnbondingDelegationResponse") proto.RegisterType((*MsgUpdateParams)(nil), "babylon.epoching.v1.MsgUpdateParams") proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.epoching.v1.MsgUpdateParamsResponse") } @@ -370,40 +453,44 @@ func init() { func init() { proto.RegisterFile("babylon/epoching/v1/tx.proto", fileDescriptor_a5fc8fed8f4e58b6) } var fileDescriptor_a5fc8fed8f4e58b6 = []byte{ - // 514 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x31, 0x6f, 0xd3, 0x40, - 0x1c, 0xc5, 0x6d, 0x82, 0x2a, 0xfa, 0x07, 0x51, 0x61, 0x22, 0x9a, 0x98, 0xc8, 0x29, 0x29, 0x08, - 0xa8, 0xe0, 0x4c, 0x8a, 0x28, 0xa2, 0x62, 0x21, 0x62, 0x42, 0x8a, 0x84, 0x8c, 0x2a, 0x04, 0x0b, - 0x3a, 0xdb, 0xa7, 0x8b, 0xd5, 0xda, 0x67, 0x7c, 0xd7, 0xaa, 0xd9, 0x10, 0x13, 0x23, 0x03, 0x1f, - 0xa0, 0x1f, 0x81, 0x81, 0x0f, 0xd1, 0x81, 0xa1, 0x62, 0x62, 0x42, 0x28, 0x19, 0xe0, 0x63, 0xa0, - 0xd8, 0x67, 0x5f, 0x70, 0x92, 0x26, 0xdd, 0x62, 0xbd, 0xf7, 0x7f, 0xbf, 0x17, 0xf9, 0xc9, 0xd0, - 0x70, 0xb1, 0xdb, 0xdf, 0x63, 0x91, 0x4d, 0x62, 0xe6, 0xf5, 0x82, 0x88, 0xda, 0x07, 0x6d, 0x5b, - 0x1c, 0xa2, 0x38, 0x61, 0x82, 0x19, 0x57, 0xa5, 0x8a, 0x72, 0x15, 0x1d, 0xb4, 0xcd, 0x2a, 0x65, - 0x94, 0xa5, 0xba, 0x3d, 0xfa, 0x95, 0x59, 0xcd, 0xa6, 0xc7, 0x78, 0xc8, 0xb8, 0xcd, 0x05, 0xde, - 0xcd, 0x62, 0x5c, 0x22, 0xb0, 0xca, 0x32, 0xd7, 0xa6, 0x91, 0x62, 0x9c, 0xe0, 0x90, 0x4b, 0x47, - 0x3d, 0x8b, 0x78, 0x97, 0x65, 0x67, 0x0f, 0x52, 0x5a, 0x95, 0xe9, 0x21, 0x4f, 0xcf, 0x42, 0x4e, - 0x33, 0xa1, 0xb5, 0x03, 0x46, 0x97, 0xd3, 0xd7, 0x09, 0x8e, 0x63, 0xe2, 0x3f, 0x27, 0x7b, 0x84, - 0x62, 0x41, 0x8c, 0x47, 0x50, 0x09, 0x39, 0xad, 0xe9, 0x6b, 0xfa, 0x9d, 0x8b, 0x9b, 0xeb, 0x48, - 0x46, 0xc9, 0x6a, 0x48, 0x56, 0x43, 0x5d, 0x4e, 0xf3, 0x0b, 0x67, 0xe4, 0xdf, 0xbe, 0xf0, 0xe9, - 0xa8, 0xa9, 0xfd, 0x3d, 0x6a, 0x6a, 0xad, 0x06, 0x98, 0x93, 0xb1, 0x0e, 0xe1, 0x31, 0x8b, 0x38, - 0x69, 0xbd, 0x81, 0xaa, 0x52, 0x77, 0x22, 0x3f, 0xc7, 0x3e, 0x1e, 0xc7, 0xde, 0x3a, 0x05, 0xab, - 0x6e, 0xca, 0x60, 0x0b, 0x1a, 0xd3, 0xa2, 0x0b, 0xb4, 0x07, 0x75, 0xa5, 0x77, 0x08, 0x0d, 0x22, - 0x87, 0x14, 0xfc, 0xa7, 0xe3, 0xfc, 0x8d, 0x53, 0xf8, 0xa5, 0xc3, 0x72, 0x89, 0x75, 0xb8, 0x31, - 0x13, 0x52, 0x34, 0xf9, 0xa2, 0xc3, 0xca, 0xe8, 0xaf, 0xc4, 0x3e, 0x16, 0xe4, 0x65, 0xfa, 0x1e, - 0x8d, 0x2d, 0x58, 0xc6, 0xfb, 0xa2, 0xc7, 0x92, 0x40, 0xf4, 0xd3, 0x1a, 0xcb, 0x9d, 0xda, 0x8f, - 0x6f, 0xf7, 0xab, 0xb2, 0xc9, 0x33, 0xdf, 0x4f, 0x08, 0xe7, 0xaf, 0x44, 0x12, 0x44, 0xd4, 0x51, - 0x56, 0xe3, 0x09, 0x2c, 0x65, 0x4b, 0xa8, 0x9d, 0x4b, 0xbb, 0x5f, 0x47, 0x53, 0x86, 0x87, 0x32, - 0x48, 0xe7, 0xfc, 0xf1, 0xaf, 0xa6, 0xe6, 0xc8, 0x83, 0xed, 0xcb, 0x1f, 0xff, 0x7c, 0xdd, 0x50, - 0x51, 0xad, 0x3a, 0xac, 0x96, 0x5a, 0xe5, 0x8d, 0x37, 0xbf, 0x57, 0xa0, 0xd2, 0xe5, 0xd4, 0xd8, - 0x85, 0x95, 0xf2, 0x60, 0x6e, 0x4f, 0x05, 0x4e, 0x4e, 0xc0, 0xb4, 0x17, 0x34, 0xe6, 0x50, 0xe3, - 0x3d, 0x5c, 0x99, 0x1c, 0xca, 0xdd, 0x39, 0x29, 0xca, 0x6a, 0xb6, 0x17, 0xb6, 0x16, 0xc8, 0x0f, - 0x3a, 0x5c, 0x9b, 0xb1, 0x10, 0x34, 0x27, 0xad, 0xe4, 0x37, 0xb7, 0xce, 0xe6, 0x2f, 0x2a, 0xb8, - 0x70, 0xe9, 0xbf, 0x61, 0xdc, 0x9c, 0x95, 0x33, 0xee, 0x32, 0xef, 0x2d, 0xe2, 0xca, 0x19, 0x9d, - 0x17, 0xc7, 0x03, 0x4b, 0x3f, 0x19, 0x58, 0xfa, 0xef, 0x81, 0xa5, 0x7f, 0x1e, 0x5a, 0xda, 0xc9, - 0xd0, 0xd2, 0x7e, 0x0e, 0x2d, 0xed, 0xed, 0x03, 0x1a, 0x88, 0xde, 0xbe, 0x8b, 0x3c, 0x16, 0xda, - 0x32, 0xd1, 0xeb, 0xe1, 0x20, 0xca, 0x1f, 0xec, 0x43, 0xf5, 0x11, 0x12, 0xfd, 0x98, 0x70, 0x77, - 0x29, 0xfd, 0x9a, 0x3c, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x69, 0x5c, 0x18, 0x0f, 0x05, - 0x00, 0x00, + // 585 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x4f, 0x6f, 0x12, 0x41, + 0x18, 0xc6, 0x77, 0x6d, 0x6d, 0xd2, 0x57, 0x63, 0x75, 0x25, 0x16, 0x56, 0xb2, 0x20, 0x68, 0x54, + 0xb4, 0xbb, 0x52, 0xb5, 0x6a, 0xe3, 0x41, 0xd1, 0x78, 0x30, 0x21, 0x31, 0x98, 0xc6, 0xc4, 0xc4, + 0x98, 0x59, 0x76, 0x32, 0x6c, 0x60, 0x67, 0xd6, 0x9d, 0x69, 0x53, 0x4e, 0x36, 0x9e, 0x3c, 0x7a, + 0xf0, 0x6c, 0xfa, 0x11, 0x7a, 0xf0, 0x43, 0xf4, 0xd8, 0x78, 0xf2, 0x64, 0x0c, 0x1c, 0xea, 0x07, + 0xf0, 0x03, 0x18, 0xf6, 0x2f, 0x02, 0x0b, 0xe8, 0x8d, 0xe1, 0x7d, 0xde, 0xe7, 0xf9, 0xc1, 0x3e, + 0x3b, 0x90, 0x37, 0x91, 0xd9, 0xed, 0x30, 0x6a, 0x60, 0x97, 0x35, 0x5b, 0x36, 0x25, 0xc6, 0x4e, + 0xd5, 0x10, 0xbb, 0xba, 0xeb, 0x31, 0xc1, 0x94, 0xf3, 0xe1, 0x54, 0x8f, 0xa6, 0xfa, 0x4e, 0x55, + 0xcd, 0x10, 0x46, 0x98, 0x3f, 0x37, 0x06, 0x9f, 0x02, 0xa9, 0x5a, 0x68, 0x32, 0xee, 0x30, 0x6e, + 0x70, 0x81, 0xda, 0x81, 0x8d, 0x89, 0x05, 0x4a, 0xbc, 0xd4, 0xe2, 0xa4, 0x24, 0x17, 0x79, 0xc8, + 0xe1, 0xa1, 0x22, 0x17, 0x58, 0xbc, 0x0d, 0xbc, 0x83, 0x43, 0x38, 0x5a, 0x0d, 0xdd, 0x1d, 0xee, + 0xaf, 0x39, 0x9c, 0x04, 0x83, 0xd2, 0x1b, 0x50, 0xea, 0x9c, 0xbc, 0xf2, 0x90, 0xeb, 0x62, 0xeb, + 0x29, 0xee, 0x60, 0x82, 0x04, 0x56, 0xee, 0xc2, 0x82, 0xc3, 0x49, 0x56, 0x2e, 0xca, 0xd7, 0x4e, + 0xad, 0x97, 0xf5, 0xd0, 0x2a, 0x44, 0xd3, 0x43, 0x34, 0xbd, 0xce, 0x49, 0xb4, 0xd1, 0x18, 0xe8, + 0x37, 0xcf, 0x7e, 0xdc, 0x2f, 0x48, 0xbf, 0xf6, 0x0b, 0xd2, 0x87, 0xe3, 0x83, 0xca, 0xe0, 0x9b, + 0x52, 0x1e, 0xd4, 0x71, 0xfb, 0x06, 0xe6, 0x2e, 0xa3, 0x1c, 0x97, 0x10, 0x64, 0x92, 0xe9, 0x16, + 0xb5, 0xa2, 0xf8, 0x7b, 0xc3, 0xf1, 0x57, 0xa6, 0xc4, 0x27, 0x3b, 0x69, 0x00, 0x1a, 0xe4, 0x27, + 0x45, 0xc4, 0x08, 0x6d, 0xc8, 0x25, 0xf3, 0x1a, 0x26, 0x36, 0x6d, 0xe0, 0x98, 0xe3, 0xe1, 0x30, + 0x47, 0x65, 0x0a, 0xc7, 0xc8, 0x62, 0x1a, 0x4c, 0x19, 0x2e, 0xa5, 0x86, 0xc5, 0x44, 0xef, 0xa1, + 0x9c, 0x88, 0x9e, 0x20, 0xda, 0xc4, 0x9d, 0x2d, 0x6a, 0x32, 0x6a, 0xd9, 0x34, 0xfa, 0xbb, 0x6d, + 0x46, 0x95, 0x67, 0xc3, 0x6c, 0x77, 0xa6, 0xb0, 0xa5, 0x5a, 0xa4, 0x51, 0xae, 0xc1, 0x8d, 0x39, + 0x00, 0x62, 0xde, 0xcf, 0x32, 0xac, 0x0c, 0x1e, 0x85, 0x6b, 0x21, 0x81, 0x5f, 0xf8, 0x7d, 0x54, + 0x36, 0x60, 0x19, 0x6d, 0x8b, 0x16, 0xf3, 0x6c, 0xd1, 0xf5, 0x11, 0x97, 0x6b, 0xd9, 0x6f, 0x5f, + 0xd7, 0x32, 0x21, 0xe5, 0x63, 0xcb, 0xf2, 0x30, 0xe7, 0x2f, 0x85, 0x67, 0x53, 0xd2, 0x48, 0xa4, + 0xca, 0x03, 0x58, 0x0a, 0x1a, 0x9d, 0x3d, 0xe1, 0xff, 0xae, 0x8b, 0xfa, 0x84, 0x17, 0x48, 0x0f, + 0x42, 0x6a, 0x8b, 0x87, 0x3f, 0x0a, 0x52, 0x23, 0x5c, 0xd8, 0x3c, 0x33, 0xe0, 0x4f, 0xac, 0x4a, + 0x39, 0x58, 0x1d, 0xa1, 0x8a, 0x88, 0xd7, 0x7f, 0x2f, 0xc2, 0x42, 0x9d, 0x13, 0xa5, 0x0d, 0x2b, + 0xa3, 0xc5, 0xbf, 0x3a, 0x31, 0x70, 0xbc, 0xc2, 0xaa, 0x31, 0xa7, 0x30, 0x0a, 0x55, 0xde, 0xc1, + 0xb9, 0xf1, 0xa2, 0x5f, 0x9f, 0xe1, 0x92, 0x48, 0xd5, 0xea, 0xdc, 0xd2, 0x38, 0x72, 0x4f, 0x86, + 0x0b, 0x29, 0xcd, 0xd6, 0x67, 0xb8, 0x8d, 0xe8, 0xd5, 0x8d, 0x7f, 0xd3, 0xc7, 0x08, 0x5f, 0x64, + 0x28, 0xce, 0xac, 0xf2, 0xfd, 0x19, 0xe6, 0xa9, 0x9b, 0xea, 0xa3, 0xff, 0xdd, 0x8c, 0x01, 0x4d, + 0x38, 0xfd, 0x57, 0x73, 0x2f, 0xa7, 0x39, 0x0e, 0xab, 0xd4, 0x9b, 0xf3, 0xa8, 0xa2, 0x0c, 0xf5, + 0xe4, 0xde, 0xf1, 0x41, 0x45, 0xae, 0x3d, 0x3f, 0xec, 0x69, 0xf2, 0x51, 0x4f, 0x93, 0x7f, 0xf6, + 0x34, 0xf9, 0x53, 0x5f, 0x93, 0x8e, 0xfa, 0x9a, 0xf4, 0xbd, 0xaf, 0x49, 0xaf, 0x6f, 0x11, 0x5b, + 0xb4, 0xb6, 0x4d, 0xbd, 0xc9, 0x1c, 0x23, 0x34, 0x6e, 0xb6, 0x90, 0x4d, 0xa3, 0x83, 0xb1, 0x9b, + 0x5c, 0xfa, 0xa2, 0xeb, 0x62, 0x6e, 0x2e, 0xf9, 0xb7, 0xf7, 0xed, 0x3f, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xdf, 0xdb, 0xcd, 0x7f, 0x7f, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -427,6 +514,9 @@ type MsgClient interface { // WrappedBeginRedelegate defines a method for performing a redelegation of // coins from a delegator and source validator to a destination validator. WrappedBeginRedelegate(ctx context.Context, in *MsgWrappedBeginRedelegate, opts ...grpc.CallOption) (*MsgWrappedBeginRedelegateResponse, error) + // WrappedCancelUnbondingDelegation defines a method for cancelling unbonding of + // coins from a delegator and source validator to a destination validator. + WrappedCancelUnbondingDelegation(ctx context.Context, in *MsgWrappedCancelUnbondingDelegation, opts ...grpc.CallOption) (*MsgWrappedCancelUnbondingDelegationResponse, error) // UpdateParams defines a method for updating epoching module parameters. UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) } @@ -466,6 +556,15 @@ func (c *msgClient) WrappedBeginRedelegate(ctx context.Context, in *MsgWrappedBe return out, nil } +func (c *msgClient) WrappedCancelUnbondingDelegation(ctx context.Context, in *MsgWrappedCancelUnbondingDelegation, opts ...grpc.CallOption) (*MsgWrappedCancelUnbondingDelegationResponse, error) { + out := new(MsgWrappedCancelUnbondingDelegationResponse) + err := c.cc.Invoke(ctx, "/babylon.epoching.v1.Msg/WrappedCancelUnbondingDelegation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { out := new(MsgUpdateParamsResponse) err := c.cc.Invoke(ctx, "/babylon.epoching.v1.Msg/UpdateParams", in, out, opts...) @@ -486,6 +585,9 @@ type MsgServer interface { // WrappedBeginRedelegate defines a method for performing a redelegation of // coins from a delegator and source validator to a destination validator. WrappedBeginRedelegate(context.Context, *MsgWrappedBeginRedelegate) (*MsgWrappedBeginRedelegateResponse, error) + // WrappedCancelUnbondingDelegation defines a method for cancelling unbonding of + // coins from a delegator and source validator to a destination validator. + WrappedCancelUnbondingDelegation(context.Context, *MsgWrappedCancelUnbondingDelegation) (*MsgWrappedCancelUnbondingDelegationResponse, error) // UpdateParams defines a method for updating epoching module parameters. UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) } @@ -503,6 +605,9 @@ func (*UnimplementedMsgServer) WrappedUndelegate(ctx context.Context, req *MsgWr func (*UnimplementedMsgServer) WrappedBeginRedelegate(ctx context.Context, req *MsgWrappedBeginRedelegate) (*MsgWrappedBeginRedelegateResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WrappedBeginRedelegate not implemented") } +func (*UnimplementedMsgServer) WrappedCancelUnbondingDelegation(ctx context.Context, req *MsgWrappedCancelUnbondingDelegation) (*MsgWrappedCancelUnbondingDelegationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WrappedCancelUnbondingDelegation not implemented") +} func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") } @@ -565,6 +670,24 @@ func _Msg_WrappedBeginRedelegate_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _Msg_WrappedCancelUnbondingDelegation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWrappedCancelUnbondingDelegation) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).WrappedCancelUnbondingDelegation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.epoching.v1.Msg/WrappedCancelUnbondingDelegation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).WrappedCancelUnbondingDelegation(ctx, req.(*MsgWrappedCancelUnbondingDelegation)) + } + return interceptor(ctx, in, info, handler) +} + 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 { @@ -599,6 +722,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "WrappedBeginRedelegate", Handler: _Msg_WrappedBeginRedelegate_Handler, }, + { + MethodName: "WrappedCancelUnbondingDelegation", + Handler: _Msg_WrappedCancelUnbondingDelegation_Handler, + }, { MethodName: "UpdateParams", Handler: _Msg_UpdateParams_Handler, @@ -782,6 +909,64 @@ func (m *MsgWrappedBeginRedelegateResponse) MarshalToSizedBuffer(dAtA []byte) (i return len(dAtA) - i, nil } +func (m *MsgWrappedCancelUnbondingDelegation) 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 *MsgWrappedCancelUnbondingDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWrappedCancelUnbondingDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Msg != nil { + { + size, err := m.Msg.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWrappedCancelUnbondingDelegationResponse) 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 *MsgWrappedCancelUnbondingDelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWrappedCancelUnbondingDelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -922,6 +1107,28 @@ func (m *MsgWrappedBeginRedelegateResponse) Size() (n int) { return n } +func (m *MsgWrappedCancelUnbondingDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Msg != nil { + l = m.Msg.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgWrappedCancelUnbondingDelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgUpdateParams) Size() (n int) { if m == nil { return 0 @@ -1360,6 +1567,142 @@ func (m *MsgWrappedBeginRedelegateResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgWrappedCancelUnbondingDelegation) 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: MsgWrappedCancelUnbondingDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWrappedCancelUnbondingDelegation: 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) + } + 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 m.Msg == nil { + m.Msg = &types.MsgCancelUnbondingDelegation{} + } + if err := m.Msg.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 *MsgWrappedCancelUnbondingDelegationResponse) 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: MsgWrappedCancelUnbondingDelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWrappedCancelUnbondingDelegationResponse: 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 (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/epoching/types/validator.go b/x/epoching/types/validator.go index 78c6cc667..70c2c63fe 100644 --- a/x/epoching/types/validator.go +++ b/x/epoching/types/validator.go @@ -3,7 +3,7 @@ package types import ( "bytes" "encoding/json" - fmt "fmt" + "fmt" "sort" "github.com/boljen/go-bitmap" @@ -13,7 +13,7 @@ import ( ) func (v *Validator) GetValAddress() sdk.ValAddress { - return sdk.ValAddress(v.Addr) + return v.Addr } func (v *Validator) GetValAddressStr() string { diff --git a/x/finality/README.md b/x/finality/README.md new file mode 100644 index 000000000..b0bd37c2c --- /dev/null +++ b/x/finality/README.md @@ -0,0 +1,378 @@ +# Finality + +Babylon's BTC Staking protocol introduces an additional consensus round on +blocks produced by CometBFT, called the finality round. The participants of this +round are referred as finality providers and their voting power stems from +staked bitcoins delegated to them. + +The Finality module is responsible for handling finality votes, maintaining the +finalization status of blocks, and identifying equivocating finality providers +in the finalization rounds. This includes: + +- handling requests for committing EOTS public randomness from finality + providers; +- handling requests for submitting finality votes from finality providers; +- maintaining the finalization status of blocks; and +- maintaining equivocation evidences of culpable finality providers. + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Concepts](#concepts) +- [States](#states) + - [Parameters](#parameters) + - [Public randomness](#public-randomness) + - [Finality votes](#finality-votes) + - [Indexed blocks with finalization status](#indexed-blocks-with-finalization-status) + - [Equivocation evidences](#equivocation-evidences) +- [Messages](#messages) + - [MsgCommitPubRandList](#msgcommitpubrandlist) + - [MsgAddFinalitySig](#msgaddfinalitysig) + - [MsgUpdateParams](#msgupdateparams) +- [EndBlocker](#endblocker) +- [Events](#events) +- [Queries](#queries) + +## Concepts + + +**Babylon Bitcoin Staking.** Babylon's Bitcoin Staking protocol allows bitcoin +holders to *trustlessly* stake their bitcoins, in order to provide economic +security to the Babylon chain and other Proof-of-Stake (PoS) blockchains. The +protocol composes a PoS blockchain with an off-the-shelf *finality voting round* +run by a set of [finality +providers](https://github.com/babylonchain/finality-provider) who receive *BTC +delegations* from [BTC stakers](https://github.com/babylonchain/btc-staker). The +finality providers and BTC delegations are maintained by Babylon's [BTC Staking +module](../btcstaking/README.md), and the Finality module is responsible for +maintaining the finality voting round. + + +**Finality voting round.** In the finality voting round, a block committed in +the CometBFT ledger receives *finality votes* from a set of finality providers. +A finality vote is a signature under the [*Extractable One-Time Signature +(EOTS)* +primitive](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf). +A block is considered finalized if it receives a quorum, i.e., votes from +finality providers with more than 2/3 voting power at its height. + + +**Slashable safety guarantee.** The finality voting round ensures the *slashable +safety* property of finalized blocks: upon a safety violation where a +conflicting block also receives a valid quorum, adversarial finality providers +with more than 1/3 total voting power will be provably identified by the +protocol and be slashed. The formal definition of slashable safety can be found +at [the S&P'23 paper](https://arxiv.org/pdf/2207.08392.pdf) and [the CCS'23 +paper](https://arxiv.org/pdf/2305.07830.pdf). In Babylon's Bitcoin Staking +protocol, if a finality provider is slashed, then + +- the secret key of the finality provider is revealed to the public, +- a parameterized amount of bitcoins of all BTC delegations under it will be + burned *on the Bitcoin network*, and +- the finality provider's voting power will be zeroized. + +In addition to the standard safety guarantee of CometBFT consensus, the +slashable safety guarantee disincentivizes safety offences launched by +adversarial finality providers. + + +**Interaction between finality providers and the Finality module.** In order to +participate in the finality voting round, an active finality provider with BTC +delegations (as specified in the [BTC Staking module](../btcstaking/README.md)) +needs to interact with the Finality module as follows: + +- **Committing EOTS public randomness.** The finality provider proactively + commits a list of *EOTS public randomness* for future heights to the Finality + module. EOTS ensures that given an EOTS public randomness, a signer can only + sign a single message. Otherwise, anyone can extract the signer's secret key + by using two EOTS signatures on different messages, the corresponding EOTS + public randomness, and the signer's public key. +- **Submitting EOTS signatures.** Upon a new block, if the finality provider has + committed an EOTS public randomness at this height, then it submits an EOTS + signature w.r.t. the committed EOTS public randomness to the Finality module. + The Finality module will verify the EOTS signature, and check if there are + known EOTS signatures on conflicting blocks from this finality provider. If + yes, then this constitutes an equivocation, and the Finality module will save + the equivocation evidence, such that anyone can extract the finality + provider's secret key and slash it. + +Babylon has implemented a [BTC staking +tracker](https://github.com/babylonchain/vigilante) daemon program that +subscribes to equivocation evidences in the Finality module, and slashes BTC +delegations under equivocating finality providers by sending their slashing +transactions to the Bitcoin network. + +## States + +The Finality module maintains the following KV stores. + +### Parameters + +The [parameter storage](./keeper/params.go) maintains the Finality module's +parameters. The Finality module's parameters are represented as a `Params` +[object](../../proto/babylon/finality/v1/params.proto) defined as follows: + +```protobuf +// Params defines the parameters for the module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // min_pub_rand is the minimum number of public randomness each + // message should commit + uint64 min_pub_rand = 1; +} +``` + +### Public randomness + +The [public randomness storage](./keeper/public_randomness.go) maintains the +list of EOTS public randomness that each finality provider commits to Babylon. +The key is the finality provider's Bitcoin secp256k1 public key concatenated +with the block height, and the value is a `SchnorrPubRand` +[object](../../types/btc_schnorr_pub_rand.go) representing the EOTS public +randomness that this finality provider commits at this height. The +`SchnorrPubRand` is a point on the secp256k1 curve, and is defined as a 32-byte +array in the implementation. + +```go +type SchnorrPubRand []byte +const SchnorrPubRandLen = 32 +``` + +### Finality votes + +The [finality vote storage](./keeper/votes.go) maintains the finality votes of +finality providers on blocks. The key is the block height concatenated with the +finality provider's Bitcoin secp256k1 public key, and the value is a +`SchnorrEOTSSig` [object](../../types/btc_schnorr_eots.go) representing an EOTS +signature. Here, the EOTS signature is signed over a block's height and +`AppHash` by the finality provider, using the private randomness corresponding +to the EOTS public randomness it commits to at the block's height. The EOTS +signature serves as a finality vote on this block from this finality provider. +It is a 32-byte scalar and is defined as a 32-byte array in the implementation. + +```go +type SchnorrEOTSSig []byte +const SchnorrEOTSSigLen = 32 +``` + +### Indexed blocks with finalization status + +The [indexed block storage](./keeper/indexed_blocks.go) maintains the necessary +metadata and finalization status of blocks. The key is the block height and the +value is an `IndexedBlock` object +[defined](../../proto/babylon/finality/v1/finality.proto) as follows. + +```protobuf +// IndexedBlock is the necessary metadata and finalization status of a block +message IndexedBlock { + // height is the height of the block + uint64 height = 1; + // app_hash is the AppHash of the block + bytes app_hash = 2; + // finalized indicates whether the IndexedBlock is finalised by 2/3 + // finality providers or not + bool finalized = 3; +} +``` + +### Equivocation evidences + +The [equivocation evidence storage](./keeper/evidence.go) maintains evidences of +equivocation offences committed by finality providers. The key is a finality +provider's Bitcoin secp256k1 public key concatenated with the block height, and +the value is an `Evidence` +[object](../../proto/babylon/finality/v1/finality.proto) representing the +evidence that this finality provider has equivocated at this height. Anyone +observing the `Evidence` object can extract the finality provider's Bitcoin +secp256k1 secret key, as per EOTS's extractability property. + +```protobuf +// Evidence is the evidence that a finality provider has signed finality +// signatures with correct public randomness on two conflicting Babylon headers +message Evidence { + // fp_btc_pk is the BTC Pk of the finality provider that casts this vote + bytes fp_btc_pk = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // block_height is the height of the conflicting blocks + uint64 block_height = 2; + // pub_rand is the public randomness the finality provider has committed to + bytes pub_rand = 3 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrPubRand" ]; + // canonical_app_hash is the AppHash of the canonical block + bytes canonical_app_hash = 4; + // fork_app_hash is the AppHash of the fork block + bytes fork_app_hash = 5; + // canonical_finality_sig is the finality signature to the canonical block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + bytes canonical_finality_sig = 6 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; + // fork_finality_sig is the finality signature to the fork block + // where finality signature is an EOTS signature + bytes fork_finality_sig = 7 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; +} +``` + +## Messages + +The Finality module handles the following messages from finality providers. The +message formats are defined at +[proto/babylon/finality/v1/tx.proto](../../proto/babylon/finality/v1/tx.proto). +The message handlers are defined at +[x/finality/keeper/msg_server.go](./keeper/msg_server.go). + +### MsgCommitPubRandList + +The `MsgCommitPubRandList` message is used for committing a list of EOTS public +randomness that will be used by a finality provider in the future. It is +typically submitted by a finality provider via the [finality +provider](https://github.com/babylonchain/finality-provider) program. + +```protobuf +// MsgCommitPubRandList defines a message for committing a list of public randomness for EOTS +message MsgCommitPubRandList { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // fp_btc_pk is the BTC PK of the finality provider that commits the public randomness + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // start_height is the start block height of the list of public randomness + uint64 start_height = 3; + // pub_rand_list is the list of public randomness + repeated bytes pub_rand_list = 4 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrPubRand" ]; + // sig is the signature on (start_height || pub_rand_list) signed by + // SK corresponding to fp_btc_pk. This prevents others to commit public + // randomness on behalf of fp_btc_pk + // TODO: another option is to restrict signer to correspond to fp_btc_pk. This restricts + // the tx submitter to be the holder of fp_btc_pk. Decide this later + bytes sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340Signature" ]; +} +``` + +Upon `MsgCommitPubRandList`, a Babylon node will execute as follows: + +1. Ensure the message contains at least `MinPubRand` number of EOTS public + randomness, where `MinPubRand` is defined in the module parameters. +2. Ensure the finality provider has been registered in Babylon. +3. Ensure the list of EOTS public randomness does not overlap with existing EOTS + public randomness that this finality provider previously committed before. +4. Verify the Schnorr signature over the list of public randomness signed by the + finality provider. +5. Store the list of EOTS public randomness to the public randomness storage. + +### MsgAddFinalitySig + +The `MsgAddFinalitySig` message is used for submitting a finality vote, i.e., an +EOTS signature over a block signed by a finality provider. It is typically +submitted by a finality provider via the [finality +provider](https://github.com/babylonchain/finality-provider) program. + +```protobuf +// MsgAddFinalitySig defines a message for adding a finality vote +message MsgAddFinalitySig { + option (cosmos.msg.v1.signer) = "signer"; + + string signer = 1; + // fp_btc_pk is the BTC PK of the finality provider that casts this vote + bytes fp_btc_pk = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BIP340PubKey" ]; + // block_height is the height of the voted block + uint64 block_height = 3; + // block_app_hash is the AppHash of the voted block + bytes block_app_hash = 4; + // finality_sig is the finality signature to this block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + bytes finality_sig = 5 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.SchnorrEOTSSig" ]; +} +``` + +Upon `MsgAddFinalitySig`, a Babylon node will execute as follows: + +1. Ensure the finality provider has been registered in Babylon and is not + slashed. +2. Ensure the finality provider has voting power at this height. +3. Ensure the finality provider has not previously casted the same vote. +4. Ensure the finality provider has a committed EOTS public randomness at this + height. +5. Verify the EOTS signature w.r.t. the committed EOTS public randomness. +6. If the voted block's `AppHash` is different from the canonical block at the + same height known by the Babylon node, then this means the finality provider + has voted for a fork. Babylon node buffers this finality vote to the evidence + storage. If the finality provider has also voted for the block at the same + height, then this finality provider is slashed, i.e., its voting power is + removed, equivocation evidence is recorded, and a slashing event is emitted. +7. If the voted block's `AppHash` is same as that of the canonical block at the + same height, then this means the finality provider has voted for the + canonical block, and the Babylon node will store this finality vote to the + finality vote storage. If the finality provider has also voted for a fork + block at the same height, then this finality provider will be slashed. + +### MsgUpdateParams + +The `MsgUpdateParams` message is used for updating the module parameters for the +Finality module. It can only be executed via a govenance proposal. + +```protobuf +// MsgUpdateParams defines a message for updating finality 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 finality parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} +``` + +## EndBlocker + +Upon `EndBlocker`, the Finality module of each Babylon node will [execute the +following](./abci.go) *if the BTC staking protocol is activated (i.e., there has +been >=1 active BTC delegations)*: + +1. Index the current block, i.e., extract its height and `AppHash`, construct an + `IndexedBlock` object, and save it to the indexed block storage. +2. Tally all non-finalized blocks as follows: + 1. Find the starting height that the Babylon node should start to finalize. + This is the earliest height that is not finalize yet since the activation + of BTC staking. + 2. For each `IndexedBlock` between the starting height and the current + height, tally this block as follows: + 1. Find the set of active finality providers at this height. + 2. If the finality provider set is empty, then this block is not + finalizable and the Babylon node will skip this block. + 3. If the finality provider set is not empty, then find all finality votes + on this `IndexedBlock`, and check whether this `IndexedBlock` has + received votes of more than 2/3 voting power from the active finality + provider set. If yes, then finalize this block, i.e., set this + `IndexedBlock` to be finalized in the indexed block storage and + distribute rewards to the voted finality providers and their BTC + delegations. Otherwise, none of the subsequent blocks shall be + finalized and the loop breaks here. + +## Events + +The Finality module defines the `EventSlashedFinalityProvider` event. It is +emitted when a finality provider is slashed due to equivocation. + +```protobuf +// EventSlashedFinalityProvider is the event emitted when a finality provider is slashed +// due to signing two conflicting blocks +message EventSlashedFinalityProvider { + // evidence is the evidence that the finality provider double signs + Evidence evidence = 1; +} +``` + +## Queries + +The Finality module provides a set of queries about finality signatures on each +block, listed at +[docs.babylonchain.io](https://docs.babylonchain.io/docs/developer-guides/grpcrestapi#tag/Finality). + diff --git a/x/finality/abci.go b/x/finality/abci.go new file mode 100644 index 000000000..facf59d75 --- /dev/null +++ b/x/finality/abci.go @@ -0,0 +1,31 @@ +package finality + +import ( + "context" + "time" + + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" +) + +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + return nil +} + +func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + + // if the BTC staking protocol is activated, i.e., there exists a height where a finality provider + // has voting power, start indexing and tallying blocks + if _, err := k.BTCStakingKeeper.GetBTCStakingActivatedHeight(ctx); err == nil { + // index the current block + k.IndexBlock(ctx) + // tally all non-finalised blocks + k.TallyBlocks(ctx) + } + + return []abci.ValidatorUpdate{}, nil +} diff --git a/x/finality/client/cli/query.go b/x/finality/client/cli/query.go new file mode 100644 index 000000000..7198e450f --- /dev/null +++ b/x/finality/client/cli/query.go @@ -0,0 +1,212 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/x/finality/types" +) + +const ( + flagQueriedBlockStatus = "queried-block-status" + flagStartHeight = "start-height" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string) *cobra.Command { + // Group finality queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand(CmdQueryParams()) + cmd.AddCommand(CmdListPublicRandomness()) + cmd.AddCommand(CmdBlock()) + cmd.AddCommand(CmdListBlocks()) + cmd.AddCommand(CmdVotesAtHeight()) + cmd.AddCommand(CmdListEvidences()) + + return cmd +} + +func CmdVotesAtHeight() *cobra.Command { + cmd := &cobra.Command{ + Use: "votes-at-height [height]", + Short: "retrieve all finality provider pks who voted at requested babylon height", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + height, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + res, err := queryClient.VotesAtHeight(cmd.Context(), &types.QueryVotesAtHeightRequest{Height: height}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdListPublicRandomness() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-public-randomness [fp_btc_pk_hex]", + Short: "list public randomness committed by a given finality provider", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.ListPublicRandomness(cmd.Context(), &types.QueryListPublicRandomnessRequest{ + FpBtcPkHex: args[0], + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "list-public-randomness") + + return cmd +} + +func CmdListBlocks() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-blocks", + Short: "list blocks at a given status", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + queriedBlockStatusString, err := cmd.Flags().GetString(flagQueriedBlockStatus) + if err != nil { + return err + } + queriedBlockStatus, err := types.NewQueriedBlockStatus(queriedBlockStatusString) + if err != nil { + return err + } + + res, err := queryClient.ListBlocks(cmd.Context(), &types.QueryListBlocksRequest{ + Status: queriedBlockStatus, + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "list-blocks") + cmd.Flags().String(flagQueriedBlockStatus, "Any", "Status of the queried blocks (NonFinalized|Finalized|Any)") + + return cmd +} + +func CmdBlock() *cobra.Command { + cmd := &cobra.Command{ + Use: "block [height]", + Short: "show the information of the block at a given height", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + queriedBlockHeight, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + res, err := queryClient.Block(cmd.Context(), &types.QueryBlockRequest{ + Height: queriedBlockHeight, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdListEvidences() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-evidences", + Short: "list equivocation evidences since a given height", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + startHeight, err := cmd.Flags().GetUint64(flagStartHeight) + if err != nil { + return err + } + + res, err := queryClient.ListEvidences(cmd.Context(), &types.QueryListEvidencesRequest{ + StartHeight: startHeight, + Pagination: pageReq, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "list-evidences") + cmd.Flags().Uint64(flagStartHeight, 0, "Starting height for scanning evidences") + + return cmd +} diff --git a/x/finality/client/cli/query_params.go b/x/finality/client/cli/query_params.go new file mode 100644 index 000000000..16ff79e70 --- /dev/null +++ b/x/finality/client/cli/query_params.go @@ -0,0 +1,36 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/x/finality/types" +) + +func CmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "shows the parameters of the module", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/finality/client/cli/tx.go b/x/finality/client/cli/tx.go new file mode 100644 index 000000000..bf4b4178b --- /dev/null +++ b/x/finality/client/cli/tx.go @@ -0,0 +1,148 @@ +package cli + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" + + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + NewCommitPubRandListCmd(), + NewAddFinalitySigCmd(), + ) + + return cmd +} + +func NewCommitPubRandListCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "commit-pubrand-list [fp_btc_pk] [start_height] [pub_rand1] [pub_rand2] ... [sig]", + Args: cobra.MinimumNArgs(4), + Short: "Commit a list of public randomness", + Long: strings.TrimSpace( + `Commit a list of public randomness.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // get finality provider BTC PK + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(args[0]) + if err != nil { + return err + } + + // get start height + startHeight, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return err + } + + // get signature + sig, err := bbn.NewBIP340SignatureFromHex(args[len(args)-1]) + if err != nil { + return err + } + + // get pub rand list + pubRandHexList := args[2 : len(args)-1] + pubRandList := []bbn.SchnorrPubRand{} + for _, prHex := range pubRandHexList { + pr, err := bbn.NewSchnorrPubRandFromHex(prHex) + if err != nil { + return err + } + pubRandList = append(pubRandList, *pr) + } + + msg := types.MsgCommitPubRandList{ + Signer: clientCtx.FromAddress.String(), + FpBtcPk: fpBTCPK, + StartHeight: startHeight, + PubRandList: pubRandList, + Sig: sig, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func NewAddFinalitySigCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "add-finality-sig [fp_btc_pk] [block_height] [block_app_hash] [finality_sig]", + Args: cobra.ExactArgs(4), + Short: "Add a finality signature", + Long: strings.TrimSpace( + `Add a finality signature.`, // TODO: example + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // get finality provider BTC PK + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(args[0]) + if err != nil { + return err + } + + // get block height + blockHeight, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return err + } + + // get block last commit hash + blockLch, err := hex.DecodeString(args[2]) + if err != nil { + return err + } + + // get finality signature + finalitySig, err := bbn.NewSchnorrEOTSSigFromHex(args[3]) + if err != nil { + return err + } + + msg := types.MsgAddFinalitySig{ + Signer: clientCtx.FromAddress.String(), + FpBtcPk: fpBTCPK, + BlockHeight: blockHeight, + BlockAppHash: blockLch, + FinalitySig: finalitySig, + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/finality/genesis.go b/x/finality/genesis.go new file mode 100644 index 000000000..6f771fb4b --- /dev/null +++ b/x/finality/genesis.go @@ -0,0 +1,22 @@ +package finality + +import ( + "context" + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns the module's exported genesis +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Params = k.GetParams(ctx) + + return genesis +} diff --git a/x/finality/genesis_test.go b/x/finality/genesis_test.go new file mode 100644 index 000000000..e99999e4e --- /dev/null +++ b/x/finality/genesis_test.go @@ -0,0 +1,26 @@ +package finality_test + +import ( + "testing" + + keepertest "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/testutil/nullify" + "github.com/babylonchain/babylon/x/finality" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/stretchr/testify/require" +) + +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + } + + k, ctx := keepertest.FinalityKeeper(t, nil, nil) + finality.InitGenesis(ctx, *k, genesisState) + got := finality.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + nullify.Fill(&genesisState) + nullify.Fill(got) + +} diff --git a/x/finality/keeper/evidence.go b/x/finality/keeper/evidence.go new file mode 100644 index 000000000..0d9f610ad --- /dev/null +++ b/x/finality/keeper/evidence.go @@ -0,0 +1,65 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) SetEvidence(ctx context.Context, evidence *types.Evidence) { + store := k.evidenceStore(ctx, evidence.FpBtcPk) + store.Set(sdk.Uint64ToBigEndian(evidence.BlockHeight), k.cdc.MustMarshal(evidence)) +} + +func (k Keeper) HasEvidence(ctx context.Context, fpBtcPK *bbn.BIP340PubKey, height uint64) bool { + store := k.evidenceStore(ctx, fpBtcPK) + return store.Has(fpBtcPK.MustMarshal()) +} + +func (k Keeper) GetEvidence(ctx context.Context, fpBtcPK *bbn.BIP340PubKey, height uint64) (*types.Evidence, error) { + if uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) < height { + return nil, types.ErrHeightTooHigh + } + store := k.evidenceStore(ctx, fpBtcPK) + evidenceBytes := store.Get(sdk.Uint64ToBigEndian(height)) + if len(evidenceBytes) == 0 { + return nil, types.ErrEvidenceNotFound + } + var evidence types.Evidence + k.cdc.MustUnmarshal(evidenceBytes, &evidence) + return &evidence, nil +} + +// GetFirstSlashableEvidence gets the first evidence that is slashable, +// i.e., it contains all fields. +// NOTE: it's possible that the CanonicalFinalitySig field is empty for +// an evidence, which happens when the finality provider signed a fork block +// but hasn't signed the canonical block yet. +func (k Keeper) GetFirstSlashableEvidence(ctx context.Context, fpBtcPK *bbn.BIP340PubKey) *types.Evidence { + store := k.evidenceStore(ctx, fpBtcPK) + iter := store.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + evidenceBytes := iter.Value() + var evidence types.Evidence + k.cdc.MustUnmarshal(evidenceBytes, &evidence) + if evidence.IsSlashable() { + return &evidence + } + } + return nil +} + +// evidenceStore returns the KVStore of the evidences +// prefix: EvidenceKey +// key: (finality provider PK || height) +// value: Evidence +func (k Keeper) evidenceStore(ctx context.Context, fpBTCPK *bbn.BIP340PubKey) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + eStore := prefix.NewStore(storeAdapter, types.EvidenceKey) + return prefix.NewStore(eStore, fpBTCPK.MustMarshal()) +} diff --git a/x/finality/keeper/grpc_query.go b/x/finality/keeper/grpc_query.go new file mode 100644 index 000000000..f6cd86d56 --- /dev/null +++ b/x/finality/keeper/grpc_query.go @@ -0,0 +1,192 @@ +package keeper + +import ( + "context" + "fmt" + "github.com/cosmos/cosmos-sdk/runtime" + + "cosmossdk.io/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" +) + +var _ types.QueryServer = Keeper{} + +// ListPublicRandomness returns a list of public randomness committed by a given +// finality provider +func (k Keeper) ListPublicRandomness(ctx context.Context, req *types.QueryListPublicRandomnessRequest) (*types.QueryListPublicRandomnessResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal finality provider BTC PK hex: %v", err) + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := k.pubRandStore(sdkCtx, fpBTCPK) + pubRandMap := map[uint64]*bbn.SchnorrPubRand{} + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + height := sdk.BigEndianToUint64(key) + pubRand, err := bbn.NewSchnorrPubRand(value) + if err != nil { + panic("failed to unmarshal EOTS public randomness in KVStore") + } + pubRandMap[height] = pubRand + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + resp := &types.QueryListPublicRandomnessResponse{ + PubRandMap: pubRandMap, + Pagination: pageRes, + } + return resp, nil +} + +func (k Keeper) Block(ctx context.Context, req *types.QueryBlockRequest) (*types.QueryBlockResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + sdkCtx := sdk.UnwrapSDKContext(ctx) + b, err := k.GetBlock(sdkCtx, req.Height) + if err != nil { + return nil, err + } + + return &types.QueryBlockResponse{Block: b}, nil +} + +// ListBlocks returns a list of blocks at the given finalisation status +func (k Keeper) ListBlocks(ctx context.Context, req *types.QueryListBlocksRequest) (*types.QueryListBlocksResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := k.blockStore(sdkCtx) + var ibs []*types.IndexedBlock + pageRes, err := query.FilteredPaginate(store, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) { + var ib types.IndexedBlock + k.cdc.MustUnmarshal(value, &ib) + + // hit if the queried status matches the block status, or the querier wants blocks in any state + if (req.Status == types.QueriedBlockStatus_FINALIZED && ib.Finalized) || + (req.Status == types.QueriedBlockStatus_NON_FINALIZED && !ib.Finalized) || + (req.Status == types.QueriedBlockStatus_ANY) { + if accumulate { + ibs = append(ibs, &ib) + } + return true, nil + } + + return false, nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + resp := &types.QueryListBlocksResponse{ + Blocks: ibs, + Pagination: pageRes, + } + return resp, nil +} + +// VotesAtHeight returns the set of votes at a given Babylon height +func (k Keeper) VotesAtHeight(ctx context.Context, req *types.QueryVotesAtHeightRequest) (*types.QueryVotesAtHeightResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + // get the sig set of babylon block at given height + btcPks := []bbn.BIP340PubKey{} + sigSet := k.GetSigSet(sdkCtx, req.Height) + for pkHex := range sigSet { + pk, err := bbn.NewBIP340PubKeyFromHex(pkHex) + if err != nil { + // failing to unmarshal finality provider BTC PK in KVStore is a programming error + panic(fmt.Errorf("%w: %w", bbn.ErrUnmarshal, err)) + } + + btcPks = append(btcPks, pk.MustMarshal()) + } + + return &types.QueryVotesAtHeightResponse{BtcPks: btcPks}, nil +} + +// Evidence returns the first evidence that allows to extract the finality provider's SK +// associated with the given finality provider's PK. +func (k Keeper) Evidence(ctx context.Context, req *types.QueryEvidenceRequest) (*types.QueryEvidenceResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + fpBTCPK, err := bbn.NewBIP340PubKeyFromHex(req.FpBtcPkHex) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal finality provider BTC PK hex: %v", err) + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + evidence := k.GetFirstSlashableEvidence(sdkCtx, fpBTCPK) + if evidence == nil { + return nil, types.ErrNoSlashableEvidence + } + + resp := &types.QueryEvidenceResponse{ + Evidence: evidence, + } + return resp, nil +} + +// ListEvidences returns a list of evidences +func (k Keeper) ListEvidences(ctx context.Context, req *types.QueryListEvidencesRequest) (*types.QueryListEvidencesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + sdkCtx := sdk.UnwrapSDKContext(ctx) + var evidences []*types.Evidence + + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + eStore := prefix.NewStore(storeAdapter, types.EvidenceKey) + + pageRes, err := query.FilteredPaginate(eStore, req.Pagination, func(key []byte, _ []byte, accumulate bool) (bool, error) { + // NOTE: we have to strip the rest bytes after the first 32 bytes + // since there is another layer of KVStore (height -> evidence) under eStore + // in which height is uint64 thus takes 8 bytes + strippedKey := key[:bbn.BIP340PubKeyLen] + fpBTCPK, err := bbn.NewBIP340PubKey(strippedKey) + if err != nil { + panic(err) // failing to unmarshal fpBTCPK in KVStore can only be a programming error + } + evidence := k.GetFirstSlashableEvidence(sdkCtx, fpBTCPK) + + // hit if the finality provider has a full evidence of equivocation + if evidence != nil && evidence.BlockHeight >= req.StartHeight { + if accumulate { + evidences = append(evidences, evidence) + } + return true, nil + } + + return false, nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + resp := &types.QueryListEvidencesResponse{ + Evidences: evidences, + Pagination: pageRes, + } + return resp, nil +} diff --git a/x/finality/keeper/grpc_query_test.go b/x/finality/keeper/grpc_query_test.go new file mode 100644 index 000000000..d9f6c1430 --- /dev/null +++ b/x/finality/keeper/grpc_query_test.go @@ -0,0 +1,329 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" +) + +func FuzzListPublicRandomness(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // add a random list of EOTS public randomness + fpBTCPK, err := datagen.GenRandomBIP340PubKey(r) + require.NoError(t, err) + startHeight := datagen.RandomInt(r, 100) + numPubRand := datagen.RandomInt(r, 1000) + 2 + _, prList, err := datagen.GenRandomPubRandList(r, numPubRand) + require.NoError(t, err) + keeper.SetPubRandList(ctx, fpBTCPK, startHeight, prList) + + // perform a query to pubrand list and assert consistency + // NOTE: pagination is already tested in Cosmos SDK so we don't test it here again, + // instead only ensure it takes effect + limit := datagen.RandomInt(r, int(numPubRand)-1) + 1 + req := &types.QueryListPublicRandomnessRequest{ + FpBtcPkHex: fpBTCPK.MarshalHex(), + Pagination: &query.PageRequest{ + Limit: limit, + }, + } + resp, err := keeper.ListPublicRandomness(ctx, req) + require.NoError(t, err) + require.Equal(t, int(limit), len(resp.PubRandMap)) // check if pagination takes effect + for i := startHeight; i < startHeight+limit; i++ { + expectedPR := prList[i-startHeight] + actualPR := resp.PubRandMap[i] + require.Equal(t, expectedPR.MustMarshal(), actualPR.MustMarshal()) + } + }) +} + +func FuzzBlock(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + height := datagen.RandomInt(r, 100) + appHash := datagen.GenRandomByteArray(r, 32) + ib := &types.IndexedBlock{ + Height: height, + AppHash: appHash, + } + + if datagen.RandomInt(r, 2) == 1 { + ib.Finalized = true + } + + keeper.SetBlock(ctx, ib) + req := &types.QueryBlockRequest{ + Height: height, + } + resp, err := keeper.Block(ctx, req) + require.NoError(t, err) + require.Equal(t, height, resp.Block.Height) + require.Equal(t, appHash, resp.Block.AppHash) + }) +} + +func FuzzListBlocks(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // index a random list of finalised blocks + startHeight := datagen.RandomInt(r, 100) + numIndexedBlocks := datagen.RandomInt(r, 100) + 1 + finalizedIndexedBlocks := make(map[uint64]*types.IndexedBlock) + nonFinalizedIndexedBlocks := make(map[uint64]*types.IndexedBlock) + indexedBlocks := make(map[uint64]*types.IndexedBlock) + for i := startHeight; i < startHeight+numIndexedBlocks; i++ { + ib := &types.IndexedBlock{ + Height: i, + AppHash: datagen.GenRandomByteArray(r, 32), + } + // randomly finalise some of them + if datagen.RandomInt(r, 2) == 1 { + ib.Finalized = true + finalizedIndexedBlocks[ib.Height] = ib + } else { + nonFinalizedIndexedBlocks[ib.Height] = ib + } + indexedBlocks[ib.Height] = ib + // insert to KVStore + keeper.SetBlock(ctx, ib) + } + + // perform a query to fetch finalized blocks and assert consistency + // NOTE: pagination is already tested in Cosmos SDK so we don't test it here again, + // instead only ensure it takes effect + if len(finalizedIndexedBlocks) != 0 { + limit := datagen.RandomInt(r, len(finalizedIndexedBlocks)) + 1 + req := &types.QueryListBlocksRequest{ + Status: types.QueriedBlockStatus_FINALIZED, + Pagination: &query.PageRequest{ + CountTotal: true, + Limit: limit, + }, + } + resp1, err := keeper.ListBlocks(ctx, req) + require.NoError(t, err) + require.LessOrEqual(t, len(resp1.Blocks), int(limit)) // check if pagination takes effect + require.EqualValues(t, resp1.Pagination.Total, len(finalizedIndexedBlocks)) + for _, actualIB := range resp1.Blocks { + require.Equal(t, finalizedIndexedBlocks[actualIB.Height].AppHash, actualIB.AppHash) + } + } + + if len(nonFinalizedIndexedBlocks) != 0 { + // perform a query to fetch non-finalized blocks and assert consistency + limit := datagen.RandomInt(r, len(nonFinalizedIndexedBlocks)) + 1 + req := &types.QueryListBlocksRequest{ + Status: types.QueriedBlockStatus_NON_FINALIZED, + Pagination: &query.PageRequest{ + CountTotal: true, + Limit: limit, + }, + } + resp2, err := keeper.ListBlocks(ctx, req) + require.NoError(t, err) + require.LessOrEqual(t, len(resp2.Blocks), int(limit)) // check if pagination takes effect + require.EqualValues(t, resp2.Pagination.Total, len(nonFinalizedIndexedBlocks)) + for _, actualIB := range resp2.Blocks { + require.Equal(t, nonFinalizedIndexedBlocks[actualIB.Height].AppHash, actualIB.AppHash) + } + } + + // perform a query to fetch all blocks and assert consistency + limit := datagen.RandomInt(r, len(indexedBlocks)) + 1 + req := &types.QueryListBlocksRequest{ + Status: types.QueriedBlockStatus_ANY, + Pagination: &query.PageRequest{ + CountTotal: true, + Limit: limit, + }, + } + resp3, err := keeper.ListBlocks(ctx, req) + require.NoError(t, err) + require.LessOrEqual(t, len(resp3.Blocks), int(limit)) // check if pagination takes effect + require.EqualValues(t, resp3.Pagination.Total, len(indexedBlocks)) + for _, actualIB := range resp3.Blocks { + require.Equal(t, indexedBlocks[actualIB.Height].AppHash, actualIB.AppHash) + } + }) +} + +func FuzzVotesAtHeight(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // Add random number of voted finality providers to the store + babylonHeight := datagen.RandomInt(r, 10) + 1 + numVotedFps := datagen.RandomInt(r, 10) + 1 + votedFpsMap := make(map[string]bool, numVotedFps) + for i := uint64(0); i < numVotedFps; i++ { + votedFpPK, err := datagen.GenRandomBIP340PubKey(r) + require.NoError(t, err) + votedSig, err := bbn.NewSchnorrEOTSSig(datagen.GenRandomByteArray(r, 32)) + require.NoError(t, err) + keeper.SetSig(ctx, babylonHeight, votedFpPK, votedSig) + + votedFpsMap[votedFpPK.MarshalHex()] = true + } + + resp, err := keeper.VotesAtHeight(ctx, &types.QueryVotesAtHeightRequest{ + Height: babylonHeight, + }) + require.NoError(t, err) + + // Check if all voted finality providers are returned + fpsFoundMap := make(map[string]bool) + for _, pk := range resp.BtcPks { + if _, ok := votedFpsMap[pk.MarshalHex()]; !ok { + t.Fatalf("rpc returned a finality provider that was not created") + } + fpsFoundMap[pk.MarshalHex()] = true + } + if len(fpsFoundMap) != len(votedFpsMap) { + t.Errorf("Some finality providers were missed. Got %d while %d were expected", len(fpsFoundMap), len(votedFpsMap)) + } + }) +} + +func FuzzQueryEvidence(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // set random BTC SK PK + sk, _, err := datagen.GenRandomBTCKeyPair(r) + bip340PK := bbn.NewBIP340PubKeyFromBTCPK(sk.PubKey()) + require.NoError(t, err) + + var randomFirstSlashableEvidence *types.Evidence = nil + numEvidences := datagen.RandomInt(r, 10) + 1 + height := uint64(5) + + // set a list of evidences, in which some of them are slashable while the others are not + for i := uint64(0); i < numEvidences; i++ { + evidence, err := datagen.GenRandomEvidence(r, sk, height) + require.NoError(t, err) + if datagen.RandomInt(r, 2) == 1 { + evidence.CanonicalFinalitySig = nil // not slashable + } else { + if randomFirstSlashableEvidence == nil { + randomFirstSlashableEvidence = evidence // first slashable + } + } + keeper.SetEvidence(ctx, evidence) + + height += datagen.RandomInt(r, 5) + 1 + } + + // get first slashable evidence + evidenceResp, err := keeper.Evidence(ctx, &types.QueryEvidenceRequest{FpBtcPkHex: bip340PK.MarshalHex()}) + if randomFirstSlashableEvidence == nil { + require.Error(t, err) + require.Nil(t, evidenceResp) + } else { + require.NoError(t, err) + require.Equal(t, randomFirstSlashableEvidence, evidenceResp.Evidence) + require.True(t, evidenceResp.Evidence.IsSlashable()) + } + }) +} + +func FuzzListEvidences(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // Setup keeper and context + keeper, ctx := testkeeper.FinalityKeeper(t, nil, nil) + ctx = sdk.UnwrapSDKContext(ctx) + + // generate a random list of evidences since startHeight + startHeight := datagen.RandomInt(r, 1000) + 100 + numEvidences := datagen.RandomInt(r, 100) + 10 + evidences := map[string]*types.Evidence{} + for i := uint64(0); i < numEvidences; i++ { + // random key pair + sk, pk, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcPK := bbn.NewBIP340PubKeyFromBTCPK(pk) + // random height + height := datagen.RandomInt(r, 100) + startHeight + 1 + // generate evidence + evidence, err := datagen.GenRandomEvidence(r, sk, height) + require.NoError(t, err) + // add evidence to map and finlaity keeper + evidences[btcPK.MarshalHex()] = evidence + keeper.SetEvidence(ctx, evidence) + } + + // generate another list of evidences before startHeight + // these evidences will not be included in the response if + // the request specifies the above startHeight + for i := uint64(0); i < numEvidences; i++ { + // random key pair + sk, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + // random height before startHeight + height := datagen.RandomInt(r, int(startHeight)) + // generate evidence + evidence, err := datagen.GenRandomEvidence(r, sk, height) + require.NoError(t, err) + // add evidence to finlaity keeper + keeper.SetEvidence(ctx, evidence) + } + + // perform a query to fetch all evidences and assert consistency + limit := datagen.RandomInt(r, int(numEvidences)) + 1 + req := &types.QueryListEvidencesRequest{ + StartHeight: startHeight, + Pagination: &query.PageRequest{ + CountTotal: true, + Limit: limit, + }, + } + resp, err := keeper.ListEvidences(ctx, req) + require.NoError(t, err) + require.LessOrEqual(t, len(resp.Evidences), int(limit)) // check if pagination takes effect + require.EqualValues(t, resp.Pagination.Total, numEvidences) // ensure evidences before startHeight are not included + for _, actualEvidence := range resp.Evidences { + require.Equal(t, evidences[actualEvidence.FpBtcPk.MarshalHex()].CanonicalAppHash, actualEvidence.CanonicalAppHash) + require.Equal(t, evidences[actualEvidence.FpBtcPk.MarshalHex()].ForkAppHash, actualEvidence.ForkAppHash) + } + }) +} diff --git a/x/finality/keeper/indexed_blocks.go b/x/finality/keeper/indexed_blocks.go new file mode 100644 index 000000000..fd7fc0771 --- /dev/null +++ b/x/finality/keeper/indexed_blocks.go @@ -0,0 +1,53 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// IndexBlock indexes the current block, saves the corresponding indexed block +// to KVStore +func (k Keeper) IndexBlock(ctx context.Context) { + headerInfo := sdk.UnwrapSDKContext(ctx).HeaderInfo() + ib := &types.IndexedBlock{ + Height: uint64(headerInfo.Height), + AppHash: headerInfo.AppHash, + Finalized: false, + } + k.SetBlock(ctx, ib) +} + +func (k Keeper) SetBlock(ctx context.Context, block *types.IndexedBlock) { + store := k.blockStore(ctx) + blockBytes := k.cdc.MustMarshal(block) + store.Set(sdk.Uint64ToBigEndian(block.Height), blockBytes) +} + +func (k Keeper) HasBlock(ctx context.Context, height uint64) bool { + store := k.blockStore(ctx) + return store.Has(sdk.Uint64ToBigEndian(height)) +} + +func (k Keeper) GetBlock(ctx context.Context, height uint64) (*types.IndexedBlock, error) { + store := k.blockStore(ctx) + blockBytes := store.Get(sdk.Uint64ToBigEndian(height)) + if len(blockBytes) == 0 { + return nil, types.ErrBlockNotFound + } + var block types.IndexedBlock + k.cdc.MustUnmarshal(blockBytes, &block) + return &block, nil +} + +// blockStore returns the KVStore of the blocks +// prefix: BlockKey +// key: block height +// value: IndexedBlock +func (k Keeper) blockStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.BlockKey) +} diff --git a/x/finality/keeper/keeper.go b/x/finality/keeper/keeper.go new file mode 100644 index 000000000..ebea17422 --- /dev/null +++ b/x/finality/keeper/keeper.go @@ -0,0 +1,47 @@ +package keeper + +import ( + "fmt" + + corestoretypes "cosmossdk.io/core/store" + + "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonchain/babylon/x/finality/types" +) + +type ( + Keeper struct { + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService + + BTCStakingKeeper types.BTCStakingKeeper + IncentiveKeeper types.IncentiveKeeper + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string + } +) + +func NewKeeper( + cdc codec.BinaryCodec, + storeService corestoretypes.KVStoreService, + btctakingKeeper types.BTCStakingKeeper, + incentiveKeeper types.IncentiveKeeper, + authority string, +) Keeper { + return Keeper{ + cdc: cdc, + storeService: storeService, + + BTCStakingKeeper: btctakingKeeper, + IncentiveKeeper: incentiveKeeper, + authority: authority, + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} diff --git a/x/finality/keeper/msg_server.go b/x/finality/keeper/msg_server.go new file mode 100644 index 000000000..6b3ced4a7 --- /dev/null +++ b/x/finality/keeper/msg_server.go @@ -0,0 +1,235 @@ +package keeper + +import ( + "bytes" + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + "github.com/babylonchain/babylon/crypto/eots" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/finality/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// UpdateParams updates the params +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) + } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := ms.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} + +// AddFinalitySig adds a new vote to a given block +func (ms msgServer) AddFinalitySig(goCtx context.Context, req *types.MsgAddFinalitySig) (*types.MsgAddFinalitySigResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // ensure the finality provider exists + fp, err := ms.BTCStakingKeeper.GetFinalityProvider(ctx, req.FpBtcPk.MustMarshal()) + if err != nil { + return nil, err + } + // ensure the finality provider is not slashed at this time point + // NOTE: it's possible that the finality provider equivocates for height h, and the signature is processed at + // height h' > h. In this case: + // - Babylon should reject any new signature from this finality provider, since it's known to be adversarial + // - Babylon should set its voting power since height h'+1 to be zero, due to the same reason + // - Babylon should NOT set its voting power between [h, h'] to be zero, since + // - Babylon BTC staking ensures safety upon 2f+1 votes, *even if* f of them are adversarial. This is + // because as long as a block gets 2f+1 votes, any other block with 2f+1 votes has a f+1 quorum + // intersection with this block, contradicting to the assumption and leading to the safety proof. + // This ensures slashable safety together with EOTS, thus does not undermine Babylon's security guarantee. + // - Due to this reason, when tallying a block, Babylon finalises this block upon 2f+1 votes. If we + // modify voting power table in the history, some finality decisions might be contradicting to the + // signature set and voting power table. + // - To fix the above issue, Babylon has to allow finalise and unfinalise blocks. However, this means + // Babylon will lose safety under an adaptive adversary corrupting even 1 finality provider. It can simply + // corrupt a new finality provider and equivocate a historical block over and over again, making a previous block + // unfinalisable forever + if fp.IsSlashed() { + return nil, bstypes.ErrFpAlreadySlashed + } + + // ensure the finality provider has voting power at this height + if req.FpBtcPk == nil { + return nil, types.ErrInvalidFinalitySig.Wrap("empty finality provider BTC PK") + } + fpPK := req.FpBtcPk + if ms.BTCStakingKeeper.GetVotingPower(ctx, fpPK.MustMarshal(), req.BlockHeight) == 0 { + return nil, types.ErrInvalidFinalitySig.Wrapf("the finality provider %v does not have voting power at height %d", fpPK.MustMarshal(), req.BlockHeight) + } + + // ensure the finality provider has not cast the same vote yet + if req.FinalitySig == nil { + return nil, types.ErrInvalidFinalitySig.Wrap("empty finality signature") + } + existingSig, err := ms.GetSig(ctx, req.BlockHeight, fpPK) + if err == nil && existingSig.Equals(req.FinalitySig) { + ms.Logger(ctx).Debug("Received duplicated finiality vote", "block height", req.BlockHeight, "finality provider", req.FpBtcPk) + // exactly same vote alreay exists, return success to the provider + return &types.MsgAddFinalitySigResponse{}, nil + } + + // ensure the finality provider has committed public randomness + pubRand, err := ms.GetPubRand(ctx, fpPK, req.BlockHeight) + if err != nil { + return nil, types.ErrPubRandNotFound + } + + // verify EOTS signature w.r.t. public randomness + fpBTCPK, err := fpPK.ToBTCPK() + if err != nil { + return nil, err + } + if err := eots.Verify(fpBTCPK, pubRand.ToFieldVal(), req.MsgToSign(), req.FinalitySig.ToModNScalar()); err != nil { + return nil, types.ErrInvalidFinalitySig.Wrapf("the EOTS signature is invalid: %v", err) + } + + // verify whether the voted block is a fork or not + indexedBlock, err := ms.GetBlock(ctx, req.BlockHeight) + if err != nil { + return nil, err + } + if !bytes.Equal(indexedBlock.AppHash, req.BlockAppHash) { + // the finality provider votes for a fork! + + // construct evidence + evidence := &types.Evidence{ + FpBtcPk: req.FpBtcPk, + BlockHeight: req.BlockHeight, + PubRand: pubRand, + CanonicalAppHash: indexedBlock.AppHash, + CanonicalFinalitySig: nil, + ForkAppHash: req.BlockAppHash, + ForkFinalitySig: req.FinalitySig, + } + + // if this finality provider has also signed canonical block, slash it + canonicalSig, err := ms.GetSig(ctx, req.BlockHeight, fpPK) + if err == nil { + //set canonial sig + evidence.CanonicalFinalitySig = canonicalSig + // slash this finality provider, including setting its voting power to + // zero, extracting its BTC SK, and emit an event + ms.slashFinalityProvider(ctx, req.FpBtcPk, evidence) + } + + // save evidence + ms.SetEvidence(ctx, evidence) + + // NOTE: we should NOT return error here, otherwise the state change triggered in this tx + // (including the evidence) will be rolled back + return &types.MsgAddFinalitySigResponse{}, nil + } + + // this signature is good, add vote to DB + ms.SetSig(ctx, req.BlockHeight, fpPK, req.FinalitySig) + + // if this finality provider has signed the canonical block before, + // slash it via extracting its secret key, and emit an event + if ms.HasEvidence(ctx, req.FpBtcPk, req.BlockHeight) { + // the finality provider has voted for a fork before! + // If this evidence is at the same height as this signature, slash this finality provider + + // get evidence + evidence, err := ms.GetEvidence(ctx, req.FpBtcPk, req.BlockHeight) + if err != nil { + panic(fmt.Errorf("failed to get evidence despite HasEvidence returns true")) + } + + // set canonical sig to this evidence + evidence.CanonicalFinalitySig = req.FinalitySig + ms.SetEvidence(ctx, evidence) + + // slash this finality provider, including setting its voting power to + // zero, extracting its BTC SK, and emit an event + ms.slashFinalityProvider(ctx, req.FpBtcPk, evidence) + } + + return &types.MsgAddFinalitySigResponse{}, nil +} + +// CommitPubRandList commits a list of EOTS public randomness +func (ms msgServer) CommitPubRandList(goCtx context.Context, req *types.MsgCommitPubRandList) (*types.MsgCommitPubRandListResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // ensure the request contains enough number of public randomness + minPubRand := ms.GetParams(ctx).MinPubRand + givenPubRand := len(req.PubRandList) + if uint64(givenPubRand) < minPubRand { + return nil, types.ErrTooFewPubRand.Wrapf("required minimum: %d, actual: %d", minPubRand, givenPubRand) + } + + // ensure the finality provider is registered + if req.FpBtcPk == nil { + return nil, types.ErrInvalidPubRand.Wrap("empty finality provider public key") + } + fpBTCPKBytes := req.FpBtcPk.MustMarshal() + if !ms.BTCStakingKeeper.HasFinalityProvider(ctx, fpBTCPKBytes) { + return nil, bstypes.ErrFpNotFound.Wrapf("the finality provider with BTC PK %v is not registered", fpBTCPKBytes) + } + + // this finality provider has not commit any public randomness, + // commit the given public randomness list and return + if ms.IsFirstPubRand(ctx, req.FpBtcPk) { + ms.SetPubRandList(ctx, req.FpBtcPk, req.StartHeight, req.PubRandList) + return &types.MsgCommitPubRandListResponse{}, nil + } + + // ensure height and req.StartHeight do not overlap, i.e., height < req.StartHeight + height, _, err := ms.GetLastPubRand(ctx, req.FpBtcPk) + if err != nil { + return nil, err + } + if height >= req.StartHeight { + return nil, types.ErrInvalidPubRand.Wrapf("the start height (%d) has overlap with the height of the highest public randomness (%d)", req.StartHeight, height) + } + + // verify signature over the list + if err := req.VerifySig(); err != nil { + return nil, types.ErrInvalidPubRand.Wrapf("invalid signature over the public randomness list: %v", err) + } + + // all good, commit the given public randomness list + ms.SetPubRandList(ctx, req.FpBtcPk, req.StartHeight, req.PubRandList) + return &types.MsgCommitPubRandListResponse{}, nil +} + +// slashFinalityProvider slashes a finality provider with the given evidence +// including setting its voting power to zero, extracting its BTC SK, +// and emit an event +func (k Keeper) slashFinalityProvider(ctx context.Context, fpBtcPk *bbn.BIP340PubKey, evidence *types.Evidence) { + // slash this finality provider, i.e., set its voting power to zero + if err := k.BTCStakingKeeper.SlashFinalityProvider(ctx, fpBtcPk.MustMarshal()); err != nil { + panic(fmt.Errorf("failed to slash finality provider: %v", err)) + } + + // emit slashing event + eventSlashing := types.NewEventSlashedFinalityProvider(evidence) + if err := sdk.UnwrapSDKContext(ctx).EventManager().EmitTypedEvent(eventSlashing); err != nil { + panic(fmt.Errorf("failed to emit EventSlashedFinalityProvider event: %w", err)) + } +} diff --git a/x/finality/keeper/msg_server_test.go b/x/finality/keeper/msg_server_test.go new file mode 100644 index 000000000..eab9337bb --- /dev/null +++ b/x/finality/keeper/msg_server_test.go @@ -0,0 +1,211 @@ +package keeper_test + +import ( + "context" + "math/rand" + "testing" + + "cosmossdk.io/core/header" + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func setupMsgServer(t testing.TB) (*keeper.Keeper, types.MsgServer, context.Context) { + fKeeper, ctx := keepertest.FinalityKeeper(t, nil, nil) + return fKeeper, keeper.NewMsgServerImpl(*fKeeper), ctx +} + +func TestMsgServer(t *testing.T) { + _, ms, ctx := setupMsgServer(t) + require.NotNil(t, ms) + require.NotNil(t, ctx) +} + +func FuzzCommitPubRandList(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, nil) + ms := keeper.NewMsgServerImpl(*fKeeper) + + // create a random finality provider + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + fpBTCPKBytes := fpBTCPK.MustMarshal() + + // Case 1: fail if the finality provider is not registered + bsKeeper.EXPECT().HasFinalityProvider(gomock.Any(), gomock.Eq(fpBTCPKBytes)).Return(false).Times(1) + startHeight := datagen.RandomInt(r, 10) + numPubRand := uint64(200) + _, msg, err := datagen.GenRandomMsgCommitPubRandList(r, btcSK, startHeight, numPubRand) + require.NoError(t, err) + _, err = ms.CommitPubRandList(ctx, msg) + require.Error(t, err) + // register the finality provider + bsKeeper.EXPECT().HasFinalityProvider(gomock.Any(), gomock.Eq(fpBTCPKBytes)).Return(true).AnyTimes() + + // Case 2: commit a list of 2/3 votes, finalise it + k.finalizeBlock(ctx, ib, voterBTCPKs) + } else { + // if not, then this block and all subsequent blocks should not be finalised + // thus, we need to break here + break + } + } else if fpSet == nil && !ib.Finalized { + // does not have finality providers, non-finalised: not finalisable, + // increment the next height to finalise and continue + k.setNextHeightToFinalize(ctx, ib.Height+1) + continue + } else if fpSet != nil && ib.Finalized { + // has finality providers and the block has finalised + // this can only be a programming error + panic(fmt.Errorf("block %d is finalized, but last finalized height in DB does not reach here", ib.Height)) + } else if fpSet == nil && ib.Finalized { + // does not have finality providers, finalised: impossible to happen, panic + panic(fmt.Errorf("block %d is finalized, but does not have a finality provider set", ib.Height)) + } + } +} + +// finalizeBlock sets a block to be finalised in KVStore and distributes rewards to +// finality providers and delegations +func (k Keeper) finalizeBlock(ctx context.Context, block *types.IndexedBlock, voterBTCPKs map[string]struct{}) { + // set block to be finalised in KVStore + block.Finalized = true + k.SetBlock(ctx, block) + // set next height to finalise as height+1 + k.setNextHeightToFinalize(ctx, block.Height+1) + // distribute rewards to BTC staking stakeholders w.r.t. the reward distribution cache + rdc, err := k.BTCStakingKeeper.GetRewardDistCache(ctx, block.Height) + if err != nil { + // failing to get a reward distribution cache before distributing reward is a programming error + panic(err) + } + // filter out voted finality providers + rdc.FilterVotedFinalityProviders(voterBTCPKs) + // reward voted finality providers + k.IncentiveKeeper.RewardBTCStaking(ctx, block.Height, rdc) + // remove reward distribution cache afterwards + k.BTCStakingKeeper.RemoveRewardDistCache(ctx, block.Height) +} + +// tally checks whether a block with the given finality provider set and votes reaches a quorum or not +func tally(fpSet map[string]uint64, voterBTCPKs map[string]struct{}) bool { + totalPower := uint64(0) + votedPower := uint64(0) + for pkStr, power := range fpSet { + totalPower += power + if _, ok := voterBTCPKs[pkStr]; ok { + votedPower += power + } + } + return votedPower*3 > totalPower*2 +} + +// setNextHeightToFinalize sets the next height to finalise as the given height +func (k Keeper) setNextHeightToFinalize(ctx context.Context, height uint64) { + store := k.storeService.OpenKVStore(ctx) + heightBytes := sdk.Uint64ToBigEndian(height) + if err := store.Set(types.NextHeightToFinalizeKey, heightBytes); err != nil { + panic(err) + } +} + +// getNextHeightToFinalize gets the next height to finalise +func (k Keeper) getNextHeightToFinalize(ctx context.Context) uint64 { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.NextHeightToFinalizeKey) + if err != nil { + panic(err) + } + if bz == nil { + return 0 + } + height := sdk.BigEndianToUint64(bz) + return height +} diff --git a/x/finality/keeper/tallying_bench_test.go b/x/finality/keeper/tallying_bench_test.go new file mode 100644 index 000000000..6155d58a8 --- /dev/null +++ b/x/finality/keeper/tallying_bench_test.go @@ -0,0 +1,93 @@ +package keeper_test + +import ( + "fmt" + "math/rand" + "os" + "runtime/pprof" + "testing" + "time" + + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func benchmarkTallyBlocks(b *testing.B, numFPs int) { + r := rand.New(rand.NewSource(time.Now().Unix())) + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + iKeeper := types.NewMockIncentiveKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(b, bsKeeper, iKeeper) + + // activate BTC staking protocol at a random height + activatedHeight := uint64(1) + // add mock queries to GetBTCStakingActivatedHeight + ctx = datagen.WithCtxHeight(ctx, uint64(activatedHeight)) + bsKeeper.EXPECT().GetBTCStakingActivatedHeight(gomock.Any()).Return(activatedHeight, nil).AnyTimes() + + // simulate fp set + fpSet := map[string]uint64{} + for i := 0; i < numFPs; i++ { + votedFpPK, err := datagen.GenRandomBIP340PubKey(r) + require.NoError(b, err) + fpSet[votedFpPK.MarshalHex()] = 1 + } + bsKeeper.EXPECT().GetVotingPowerTable(gomock.Any(), gomock.Any()).Return(fpSet).AnyTimes() + + // TODO: test incentive + bsKeeper.EXPECT().GetRewardDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewRewardDistCache(), nil).AnyTimes() + iKeeper.EXPECT().RewardBTCStaking(gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes() + bsKeeper.EXPECT().RemoveRewardDistCache(gomock.Any(), gomock.Any()).Return().AnyTimes() + // Start the CPU profiler + cpuProfileFile := fmt.Sprintf("/tmp/finality-tally-blocks-%d-cpu.pprof", numFPs) + f, err := os.Create(cpuProfileFile) + if err != nil { + b.Fatal(err) + } + defer f.Close() + if err := pprof.StartCPUProfile(f); err != nil { + b.Fatal(err) + } + defer pprof.StopCPUProfile() + + // Reset timer before the benchmark loop starts + b.ResetTimer() + + // tally a block + for i := int(activatedHeight); i < b.N; i++ { + b.StopTimer() + + height := uint64(i) + // increment height in ctx + ctx = datagen.WithCtxHeight(ctx, height) + // index blocks + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: height, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: false, + }) + // give votes to the block + for fpPKHex := range fpSet { + votedFpPK, err := bbn.NewBIP340PubKeyFromHex(fpPKHex) + require.NoError(b, err) + votedSig, err := bbn.NewSchnorrEOTSSig(datagen.GenRandomByteArray(r, 32)) + require.NoError(b, err) + fKeeper.SetSig(ctx, height, votedFpPK, votedSig) + } + + b.StartTimer() + + fKeeper.TallyBlocks(ctx) + } +} + +func BenchmarkTallyBlocks_10(b *testing.B) { benchmarkTallyBlocks(b, 10) } +func BenchmarkTallyBlocks_50(b *testing.B) { benchmarkTallyBlocks(b, 50) } +func BenchmarkTallyBlocks_100(b *testing.B) { benchmarkTallyBlocks(b, 100) } diff --git a/x/finality/keeper/tallying_test.go b/x/finality/keeper/tallying_test.go new file mode 100644 index 000000000..aa4af547a --- /dev/null +++ b/x/finality/keeper/tallying_test.go @@ -0,0 +1,193 @@ +package keeper_test + +import ( + "encoding/hex" + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzTallying_PanicCases(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + iKeeper := types.NewMockIncentiveKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper) + + // Case 1: expect to panic if tallying upon BTC staking protocol is not activated + bsKeeper.EXPECT().GetBTCStakingActivatedHeight(gomock.Any()).Return(uint64(0), bstypes.ErrBTCStakingNotActivated).Times(1) + require.Panics(t, func() { fKeeper.TallyBlocks(ctx) }) + + // Case 2: expect to panic if finalised block with nil finality provider + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: 1, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: true, + }) + // activate BTC staking protocol at height 1 + ctx = datagen.WithCtxHeight(ctx, 1) + bsKeeper.EXPECT().GetBTCStakingActivatedHeight(gomock.Any()).Return(uint64(1), nil).Times(1) + bsKeeper.EXPECT().GetVotingPowerTable(gomock.Any(), gomock.Eq(uint64(1))).Return(nil).Times(1) + require.Panics(t, func() { fKeeper.TallyBlocks(ctx) }) + }) +} + +func FuzzTallying_FinalizingNoBlock(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + iKeeper := types.NewMockIncentiveKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper) + + // activate BTC staking protocol at a random height + activatedHeight := datagen.RandomInt(r, 10) + 1 + + // index a list of blocks, don't give them QCs, and tally them + // Expect they are not finalised + for i := activatedHeight; i < activatedHeight+10; i++ { + // index blocks + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: i, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: false, + }) + // this block does not have QC + err := giveNoQCToHeight(r, ctx, bsKeeper, fKeeper, i) + require.NoError(t, err) + } + // add mock queries to GetBTCStakingActivatedHeight + ctx = datagen.WithCtxHeight(ctx, activatedHeight+10-1) + bsKeeper.EXPECT().GetBTCStakingActivatedHeight(gomock.Any()).Return(activatedHeight, nil).Times(1) + // tally blocks and none of them should be finalised + fKeeper.TallyBlocks(ctx) + for i := activatedHeight; i < activatedHeight+10; i++ { + ib, err := fKeeper.GetBlock(ctx, i) + require.NoError(t, err) + require.False(t, ib.Finalized) + } + }) + +} + +func FuzzTallying_FinalizingSomeBlocks(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + iKeeper := types.NewMockIncentiveKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper) + + // activate BTC staking protocol at a random height + activatedHeight := datagen.RandomInt(r, 10) + 1 + + // index a list of blocks, give some of them QCs, and tally them. + // Expect they are all finalised + numWithQCs := datagen.RandomInt(r, 5) + 1 + for i := activatedHeight; i < activatedHeight+10; i++ { + // index blocks + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: i, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: false, + }) + if i < activatedHeight+numWithQCs { + // this block has QC + err := giveQCToHeight(r, ctx, bsKeeper, fKeeper, i) + require.NoError(t, err) + } else { + // this block does not have QC + err := giveNoQCToHeight(r, ctx, bsKeeper, fKeeper, i) + require.NoError(t, err) + } + } + // we don't test incentive in this function + bsKeeper.EXPECT().GetRewardDistCache(gomock.Any(), gomock.Any()).Return(bstypes.NewRewardDistCache(), nil).Times(int(numWithQCs)) + iKeeper.EXPECT().RewardBTCStaking(gomock.Any(), gomock.Any(), gomock.Any()).Return().Times(int(numWithQCs)) + bsKeeper.EXPECT().RemoveRewardDistCache(gomock.Any(), gomock.Any()).Return().Times(int(numWithQCs)) + // add mock queries to GetBTCStakingActivatedHeight + ctx = datagen.WithCtxHeight(ctx, activatedHeight+10-1) + bsKeeper.EXPECT().GetBTCStakingActivatedHeight(gomock.Any()).Return(activatedHeight, nil).Times(1) + // tally blocks and none of them should be finalised + fKeeper.TallyBlocks(ctx) + for i := activatedHeight; i < activatedHeight+10; i++ { + ib, err := fKeeper.GetBlock(ctx, i) + require.NoError(t, err) + if i < activatedHeight+numWithQCs { + require.True(t, ib.Finalized) + } else { + require.False(t, ib.Finalized) + } + } + }) + +} + +func giveQCToHeight(r *rand.Rand, ctx sdk.Context, bsKeeper *types.MockBTCStakingKeeper, fKeeper *keeper.Keeper, height uint64) error { + // 4 finality providers + fpSet := map[string]uint64{} + // 3 votes + for i := 0; i < 3; i++ { + votedFpPK, err := datagen.GenRandomBIP340PubKey(r) + if err != nil { + return err + } + votedSig, err := bbn.NewSchnorrEOTSSig(datagen.GenRandomByteArray(r, 32)) + if err != nil { + return err + } + fKeeper.SetSig(ctx, height, votedFpPK, votedSig) + // add finality provider + fpSet[votedFpPK.MarshalHex()] = 1 + } + // the rest of the finality providers do not vote + fpSet[hex.EncodeToString(datagen.GenRandomByteArray(r, 32))] = 1 + bsKeeper.EXPECT().GetVotingPowerTable(gomock.Any(), gomock.Eq(height)).Return(fpSet).Times(1) + + return nil +} + +func giveNoQCToHeight(r *rand.Rand, ctx sdk.Context, bsKeeper *types.MockBTCStakingKeeper, fKeeper *keeper.Keeper, height uint64) error { + // 1 vote + votedFpPK, err := datagen.GenRandomBIP340PubKey(r) + if err != nil { + return err + } + votedSig, err := bbn.NewSchnorrEOTSSig(datagen.GenRandomByteArray(r, 32)) + if err != nil { + return err + } + fKeeper.SetSig(ctx, height, votedFpPK, votedSig) + // 4 finality providers + fpSet := map[string]uint64{ + votedFpPK.MarshalHex(): 1, + hex.EncodeToString(datagen.GenRandomByteArray(r, 32)): 1, + hex.EncodeToString(datagen.GenRandomByteArray(r, 32)): 1, + hex.EncodeToString(datagen.GenRandomByteArray(r, 32)): 1, + } + bsKeeper.EXPECT().GetVotingPowerTable(gomock.Any(), gomock.Eq(height)).Return(fpSet).MaxTimes(1) + + return nil +} diff --git a/x/finality/keeper/votes.go b/x/finality/keeper/votes.go new file mode 100644 index 000000000..44bd6352b --- /dev/null +++ b/x/finality/keeper/votes.go @@ -0,0 +1,101 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/runtime" + + "cosmossdk.io/store/prefix" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) SetSig(ctx context.Context, height uint64, fpBtcPK *bbn.BIP340PubKey, sig *bbn.SchnorrEOTSSig) { + store := k.voteStore(ctx, height) + store.Set(fpBtcPK.MustMarshal(), sig.MustMarshal()) +} + +func (k Keeper) HasSig(ctx context.Context, height uint64, fpBtcPK *bbn.BIP340PubKey) bool { + store := k.voteStore(ctx, height) + return store.Has(fpBtcPK.MustMarshal()) +} + +func (k Keeper) GetSig(ctx context.Context, height uint64, fpBtcPK *bbn.BIP340PubKey) (*bbn.SchnorrEOTSSig, error) { + if uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) < height { + return nil, types.ErrHeightTooHigh + } + store := k.voteStore(ctx, height) + sigBytes := store.Get(fpBtcPK.MustMarshal()) + if len(sigBytes) == 0 { + return nil, types.ErrVoteNotFound + } + sig, err := bbn.NewSchnorrEOTSSig(sigBytes) + if err != nil { + panic(fmt.Errorf("failed to unmarshal EOTS signature: %w", err)) + } + return sig, nil +} + +// GetSigSet gets all EOTS signatures at a given height +func (k Keeper) GetSigSet(ctx context.Context, height uint64) map[string]*bbn.SchnorrEOTSSig { + store := k.voteStore(ctx, height) + iter := store.Iterator(nil, nil) + defer iter.Close() + + // if there is no vote on this height, return nil + if !iter.Valid() { + return nil + } + + sigs := map[string]*bbn.SchnorrEOTSSig{} + for ; iter.Valid(); iter.Next() { + fpBTCPK, err := bbn.NewBIP340PubKey(iter.Key()) + if err != nil { + // failing to unmarshal finality provider's BTC PK in KVStore is a programming error + panic(fmt.Errorf("%w: %w", bbn.ErrUnmarshal, err)) + } + sig, err := bbn.NewSchnorrEOTSSig(iter.Value()) + if err != nil { + // failing to unmarshal EOTS sig in KVStore is a programming error + panic(fmt.Errorf("failed to unmarshal EOTS signature: %w", err)) + } + sigs[fpBTCPK.MarshalHex()] = sig + } + return sigs +} + +// GetVoters gets returns a map of voters' BTC PKs to the given height +func (k Keeper) GetVoters(ctx context.Context, height uint64) map[string]struct{} { + store := k.voteStore(ctx, height) + iter := store.Iterator(nil, nil) + defer iter.Close() + + // if there is no vote on this height, return nil + if !iter.Valid() { + return nil + } + + voterBTCPKs := map[string]struct{}{} + for ; iter.Valid(); iter.Next() { + // accumulate voterBTCPKs + fpBTCPK, err := bbn.NewBIP340PubKey(iter.Key()) + if err != nil { + // failing to unmarshal finality provider's BTC PK in KVStore is a programming error + panic(fmt.Errorf("%w: %w", bbn.ErrUnmarshal, err)) + } + voterBTCPKs[fpBTCPK.MarshalHex()] = struct{}{} + } + return voterBTCPKs +} + +// voteStore returns the KVStore of the votes +// prefix: VoteKey +// key: (block height || finality provider PK) +// value: EOTS sig +func (k Keeper) voteStore(ctx context.Context, height uint64) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + prefixedStore := prefix.NewStore(storeAdapter, types.VoteKey) + return prefix.NewStore(prefixedStore, sdk.Uint64ToBigEndian(height)) +} diff --git a/x/finality/keeper/votes_bench_test.go b/x/finality/keeper/votes_bench_test.go new file mode 100644 index 000000000..c22a6cba4 --- /dev/null +++ b/x/finality/keeper/votes_bench_test.go @@ -0,0 +1,85 @@ +package keeper_test + +import ( + "math/rand" + "os" + "runtime/pprof" + "testing" + "time" + + "cosmossdk.io/core/header" + "github.com/babylonchain/babylon/testutil/datagen" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func benchmarkAddFinalitySig(b *testing.B) { + r := rand.New(rand.NewSource(time.Now().Unix())) + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(b, bsKeeper, nil) + ms := keeper.NewMsgServerImpl(*fKeeper) + + // create a random finality provider + btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(b, err) + fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) + fpBTCPKBytes := fpBTCPK.MustMarshal() + fp, err := datagen.GenRandomFinalityProviderWithBTCSK(r, btcSK) + require.NoError(b, err) + + // register the finality provider + bsKeeper.EXPECT().HasFinalityProvider(gomock.Any(), gomock.Eq(fpBTCPKBytes)).Return(true).AnyTimes() + bsKeeper.EXPECT().GetFinalityProvider(gomock.Any(), gomock.Eq(fpBTCPKBytes)).Return(fp, nil).AnyTimes() + // mock voting power + bsKeeper.EXPECT().GetVotingPower(gomock.Any(), gomock.Eq(fpBTCPKBytes), gomock.Any()).Return(uint64(1)).AnyTimes() + + // commit enough public randomness + // TODO: generalise commit public randomness to allow arbitrary benchtime + srList, msg, err := datagen.GenRandomMsgCommitPubRandList(r, btcSK, 0, 100000) + require.NoError(b, err) + _, err = ms.CommitPubRandList(ctx, msg) + require.NoError(b, err) + + // Start the CPU profiler + cpuProfileFile := "/tmp/finality-submit-finality-sig-cpu.pprof" + f, err := os.Create(cpuProfileFile) + if err != nil { + b.Fatal(err) + } + defer f.Close() + if err := pprof.StartCPUProfile(f); err != nil { + b.Fatal(err) + } + defer pprof.StopCPUProfile() + + // Reset timer before the benchmark loop starts + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + + height := uint64(i) + + // generate a vote + sr := srList[height] + blockHash := datagen.GenRandomByteArray(r, 32) + signer := datagen.GenRandomAccount().Address + msg, err := types.NewMsgAddFinalitySig(signer, btcSK, sr, height, blockHash) + require.NoError(b, err) + ctx = ctx.WithHeaderInfo(header.Info{Height: int64(height), AppHash: blockHash}) + + b.StartTimer() + + _, err = ms.AddFinalitySig(ctx, msg) + require.Error(b, err) + } +} + +func BenchmarkAddFinalitySig(b *testing.B) { benchmarkAddFinalitySig(b) } diff --git a/x/finality/module.go b/x/finality/module.go new file mode 100644 index 000000000..81a1f8df0 --- /dev/null +++ b/x/finality/module.go @@ -0,0 +1,150 @@ +package finality + +import ( + "context" + "cosmossdk.io/core/appmodule" + "encoding/json" + "fmt" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/babylonchain/babylon/x/finality/client/cli" + "github.com/babylonchain/babylon/x/finality/keeper" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck // generally we don't handle errors here +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted) +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) +} + +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { + return EndBlocker(ctx, am.keeper) +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker +} diff --git a/x/finality/types/codec.go b/x/finality/types/codec.go new file mode 100644 index 000000000..04f766fa3 --- /dev/null +++ b/x/finality/types/codec.go @@ -0,0 +1,31 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgAddFinalitySig{}, "finality/MsgAddFinalitySig", nil) + cdc.RegisterConcrete(&MsgCommitPubRandList{}, "finality/MsgCommitPubRandList", nil) + cdc.RegisterConcrete(&MsgUpdateParams{}, "finality/MsgUpdateParams", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + // Register messages + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgAddFinalitySig{}, + &MsgCommitPubRandList{}, + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/finality/types/errors.go b/x/finality/types/errors.go new file mode 100644 index 000000000..b39e4ff37 --- /dev/null +++ b/x/finality/types/errors.go @@ -0,0 +1,19 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +// x/finality module sentinel errors +var ( + ErrBlockNotFound = errorsmod.Register(ModuleName, 1100, "Block is not found") + ErrVoteNotFound = errorsmod.Register(ModuleName, 1101, "vote is not found") + ErrHeightTooHigh = errorsmod.Register(ModuleName, 1102, "the chain has not reached the given height yet") + ErrPubRandNotFound = errorsmod.Register(ModuleName, 1103, "public randomness is not found") + ErrNoPubRandYet = errorsmod.Register(ModuleName, 1104, "the finality provider has not committed any public randomness yet") + ErrTooFewPubRand = errorsmod.Register(ModuleName, 1105, "the request contains too few public randomness") + ErrInvalidPubRand = errorsmod.Register(ModuleName, 1106, "the public randomness list is invalid") + ErrEvidenceNotFound = errorsmod.Register(ModuleName, 1107, "evidence is not found") + ErrInvalidFinalitySig = errorsmod.Register(ModuleName, 1108, "finality signature is not valid") + ErrNoSlashableEvidence = errorsmod.Register(ModuleName, 1109, "there is no slashable evidence") +) diff --git a/x/finality/types/events.go b/x/finality/types/events.go new file mode 100644 index 000000000..6d452f5d0 --- /dev/null +++ b/x/finality/types/events.go @@ -0,0 +1,7 @@ +package types + +func NewEventSlashedFinalityProvider(evidence *Evidence) *EventSlashedFinalityProvider { + return &EventSlashedFinalityProvider{ + Evidence: evidence, + } +} diff --git a/x/finality/types/events.pb.go b/x/finality/types/events.pb.go new file mode 100644 index 000000000..2a19e5f20 --- /dev/null +++ b/x/finality/types/events.pb.go @@ -0,0 +1,329 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/v1/events.proto + +package types + +import ( + fmt "fmt" + 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 + +// EventSlashedFinalityProvider is the event emitted when a finality provider is slashed +// due to signing two conflicting blocks +type EventSlashedFinalityProvider struct { + // evidence is the evidence that the finality provider double signs + Evidence *Evidence `protobuf:"bytes,1,opt,name=evidence,proto3" json:"evidence,omitempty"` +} + +func (m *EventSlashedFinalityProvider) Reset() { *m = EventSlashedFinalityProvider{} } +func (m *EventSlashedFinalityProvider) String() string { return proto.CompactTextString(m) } +func (*EventSlashedFinalityProvider) ProtoMessage() {} +func (*EventSlashedFinalityProvider) Descriptor() ([]byte, []int) { + return fileDescriptor_c34c03aae5e3e6bf, []int{0} +} +func (m *EventSlashedFinalityProvider) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventSlashedFinalityProvider) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventSlashedFinalityProvider.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 *EventSlashedFinalityProvider) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventSlashedFinalityProvider.Merge(m, src) +} +func (m *EventSlashedFinalityProvider) XXX_Size() int { + return m.Size() +} +func (m *EventSlashedFinalityProvider) XXX_DiscardUnknown() { + xxx_messageInfo_EventSlashedFinalityProvider.DiscardUnknown(m) +} + +var xxx_messageInfo_EventSlashedFinalityProvider proto.InternalMessageInfo + +func (m *EventSlashedFinalityProvider) GetEvidence() *Evidence { + if m != nil { + return m.Evidence + } + return nil +} + +func init() { + proto.RegisterType((*EventSlashedFinalityProvider)(nil), "babylon.finality.v1.EventSlashedFinalityProvider") +} + +func init() { proto.RegisterFile("babylon/finality/v1/events.proto", fileDescriptor_c34c03aae5e3e6bf) } + +var fileDescriptor_c34c03aae5e3e6bf = []byte{ + // 193 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0xcb, 0xcc, 0x4b, 0xcc, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, 0xd4, + 0x4f, 0x2d, 0x4b, 0xcd, 0x2b, 0x29, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0xaa, + 0xd0, 0x83, 0xa9, 0xd0, 0x2b, 0x33, 0x94, 0x52, 0xc2, 0xa6, 0x0d, 0xae, 0x00, 0xac, 0x51, 0x29, + 0x92, 0x4b, 0xc6, 0x15, 0x64, 0x50, 0x70, 0x4e, 0x62, 0x71, 0x46, 0x6a, 0x8a, 0x1b, 0x54, 0x36, + 0xa0, 0x28, 0xbf, 0x2c, 0x33, 0x25, 0xb5, 0x48, 0xc8, 0x92, 0x8b, 0x23, 0x15, 0xc4, 0xca, 0x4b, + 0x4e, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd5, 0xc3, 0x62, 0x97, 0x9e, 0x2b, 0x54, + 0x51, 0x10, 0x5c, 0xb9, 0x93, 0xd7, 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, + 0x19, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0x0d, 0x4b, 0xce, + 0x48, 0xcc, 0xcc, 0x83, 0x71, 0xf4, 0x2b, 0x10, 0x4e, 0x2e, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, + 0x03, 0xbb, 0xd6, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x4e, 0xb5, 0x88, 0x0a, 0x01, 0x00, + 0x00, +} + +func (m *EventSlashedFinalityProvider) 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 *EventSlashedFinalityProvider) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventSlashedFinalityProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Evidence != nil { + { + size, err := m.Evidence.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventSlashedFinalityProvider) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Evidence != nil { + l = m.Evidence.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventSlashedFinalityProvider) 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 ErrIntOverflowEvents + } + 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: EventSlashedFinalityProvider: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventSlashedFinalityProvider: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Evidence == nil { + m.Evidence = &Evidence{} + } + if err := m.Evidence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/finality/types/expected_keepers.go b/x/finality/types/expected_keepers.go new file mode 100644 index 000000000..d13913622 --- /dev/null +++ b/x/finality/types/expected_keepers.go @@ -0,0 +1,23 @@ +package types + +import ( + "context" + + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" +) + +type BTCStakingKeeper interface { + GetFinalityProvider(ctx context.Context, fpBTCPK []byte) (*bstypes.FinalityProvider, error) + HasFinalityProvider(ctx context.Context, fpBTCPK []byte) bool + SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error + GetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64) uint64 + GetVotingPowerTable(ctx context.Context, height uint64) map[string]uint64 + GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) + GetRewardDistCache(ctx context.Context, height uint64) (*bstypes.RewardDistCache, error) + RemoveRewardDistCache(ctx context.Context, height uint64) +} + +// IncentiveKeeper defines the expected interface needed to distribute rewards. +type IncentiveKeeper interface { + RewardBTCStaking(ctx context.Context, height uint64, rdc *bstypes.RewardDistCache) +} diff --git a/x/finality/types/finality.go b/x/finality/types/finality.go new file mode 100644 index 000000000..5e1380d1d --- /dev/null +++ b/x/finality/types/finality.go @@ -0,0 +1,84 @@ +package types + +import ( + "bytes" + "fmt" + + "github.com/babylonchain/babylon/crypto/eots" + "github.com/btcsuite/btcd/btcec/v2" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// msgToSignForVote returns the message for an EOTS signature +// The EOTS signature on a block will be (blockHeight || blockHash) +func msgToSignForVote(blockHeight uint64, blockHash []byte) []byte { + return append(sdk.Uint64ToBigEndian(blockHeight), blockHash...) +} + +func (ib *IndexedBlock) Equal(ib2 *IndexedBlock) bool { + if !bytes.Equal(ib.AppHash, ib2.AppHash) { + return false + } + if ib.Height != ib2.Height { + return false + } + // NOTE: we don't compare finalisation status here + return true +} + +func (ib *IndexedBlock) MsgToSign() []byte { + return msgToSignForVote(ib.Height, ib.AppHash) +} + +func (e *Evidence) canonicalMsgToSign() []byte { + return msgToSignForVote(e.BlockHeight, e.CanonicalAppHash) +} + +func (e *Evidence) forkMsgToSign() []byte { + return msgToSignForVote(e.BlockHeight, e.ForkAppHash) +} + +func (e *Evidence) ValidateBasic() error { + if e.FpBtcPk == nil { + return fmt.Errorf("empty FpBtcPk") + } + if e.PubRand == nil { + return fmt.Errorf("empty PubRand") + } + if len(e.CanonicalAppHash) != 32 { + return fmt.Errorf("malformed CanonicalAppHash") + } + if len(e.ForkAppHash) != 32 { + return fmt.Errorf("malformed ForkAppHash") + } + if e.ForkFinalitySig == nil { + return fmt.Errorf("empty ForkFinalitySig") + } + return nil +} + +func (e *Evidence) IsSlashable() bool { + if err := e.ValidateBasic(); err != nil { + return false + } + if e.CanonicalFinalitySig == nil { + return false + } + return true +} + +// ExtractBTCSK extracts the BTC SK given the data in the evidence +func (e *Evidence) ExtractBTCSK() (*btcec.PrivateKey, error) { + if !e.IsSlashable() { + return nil, fmt.Errorf("the evidence lacks some fields so does not allow extracting BTC SK") + } + btcPK, err := e.FpBtcPk.ToBTCPK() + if err != nil { + return nil, err + } + return eots.Extract( + btcPK, e.PubRand.ToFieldVal(), + e.canonicalMsgToSign(), e.CanonicalFinalitySig.ToModNScalar(), // msg and sig for canonical block + e.forkMsgToSign(), e.ForkFinalitySig.ToModNScalar(), // msg and sig for fork block + ) +} diff --git a/x/finality/types/finality.pb.go b/x/finality/types/finality.pb.go new file mode 100644 index 000000000..7f1ebb971 --- /dev/null +++ b/x/finality/types/finality.pb.go @@ -0,0 +1,899 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/v1/finality.proto + +package types + +import ( + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "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 + +// IndexedBlock is the necessary metadata and finalization status of a block +type IndexedBlock struct { + // height is the height of the block + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // app_hash is the AppHash of the block + AppHash []byte `protobuf:"bytes,2,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // finalized indicates whether the IndexedBlock is finalised by 2/3 + // finality providers or not + Finalized bool `protobuf:"varint,3,opt,name=finalized,proto3" json:"finalized,omitempty"` +} + +func (m *IndexedBlock) Reset() { *m = IndexedBlock{} } +func (m *IndexedBlock) String() string { return proto.CompactTextString(m) } +func (*IndexedBlock) ProtoMessage() {} +func (*IndexedBlock) Descriptor() ([]byte, []int) { + return fileDescriptor_ca5b87e52e3e6d02, []int{0} +} +func (m *IndexedBlock) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IndexedBlock) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IndexedBlock.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 *IndexedBlock) XXX_Merge(src proto.Message) { + xxx_messageInfo_IndexedBlock.Merge(m, src) +} +func (m *IndexedBlock) XXX_Size() int { + return m.Size() +} +func (m *IndexedBlock) XXX_DiscardUnknown() { + xxx_messageInfo_IndexedBlock.DiscardUnknown(m) +} + +var xxx_messageInfo_IndexedBlock proto.InternalMessageInfo + +func (m *IndexedBlock) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *IndexedBlock) GetAppHash() []byte { + if m != nil { + return m.AppHash + } + return nil +} + +func (m *IndexedBlock) GetFinalized() bool { + if m != nil { + return m.Finalized + } + return false +} + +// Evidence is the evidence that a finality provider has signed finality +// signatures with correct public randomness on two conflicting Babylon headers +type Evidence struct { + // fp_btc_pk is the BTC PK of the finality provider that casts this vote + FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"` + // block_height is the height of the conflicting blocks + BlockHeight uint64 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + // pub_rand is the public randomness the finality provider has committed to + PubRand *github_com_babylonchain_babylon_types.SchnorrPubRand `protobuf:"bytes,3,opt,name=pub_rand,json=pubRand,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrPubRand" json:"pub_rand,omitempty"` + // canonical_app_hash is the AppHash of the canonical block + CanonicalAppHash []byte `protobuf:"bytes,4,opt,name=canonical_app_hash,json=canonicalAppHash,proto3" json:"canonical_app_hash,omitempty"` + // fork_app_hash is the AppHash of the fork block + ForkAppHash []byte `protobuf:"bytes,5,opt,name=fork_app_hash,json=forkAppHash,proto3" json:"fork_app_hash,omitempty"` + // canonical_finality_sig is the finality signature to the canonical block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + CanonicalFinalitySig *github_com_babylonchain_babylon_types.SchnorrEOTSSig `protobuf:"bytes,6,opt,name=canonical_finality_sig,json=canonicalFinalitySig,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrEOTSSig" json:"canonical_finality_sig,omitempty"` + // fork_finality_sig is the finality signature to the fork block + // where finality signature is an EOTS signature + ForkFinalitySig *github_com_babylonchain_babylon_types.SchnorrEOTSSig `protobuf:"bytes,7,opt,name=fork_finality_sig,json=forkFinalitySig,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrEOTSSig" json:"fork_finality_sig,omitempty"` +} + +func (m *Evidence) Reset() { *m = Evidence{} } +func (m *Evidence) String() string { return proto.CompactTextString(m) } +func (*Evidence) ProtoMessage() {} +func (*Evidence) Descriptor() ([]byte, []int) { + return fileDescriptor_ca5b87e52e3e6d02, []int{1} +} +func (m *Evidence) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Evidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Evidence.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 *Evidence) XXX_Merge(src proto.Message) { + xxx_messageInfo_Evidence.Merge(m, src) +} +func (m *Evidence) XXX_Size() int { + return m.Size() +} +func (m *Evidence) XXX_DiscardUnknown() { + xxx_messageInfo_Evidence.DiscardUnknown(m) +} + +var xxx_messageInfo_Evidence proto.InternalMessageInfo + +func (m *Evidence) GetBlockHeight() uint64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *Evidence) GetCanonicalAppHash() []byte { + if m != nil { + return m.CanonicalAppHash + } + return nil +} + +func (m *Evidence) GetForkAppHash() []byte { + if m != nil { + return m.ForkAppHash + } + return nil +} + +func init() { + proto.RegisterType((*IndexedBlock)(nil), "babylon.finality.v1.IndexedBlock") + proto.RegisterType((*Evidence)(nil), "babylon.finality.v1.Evidence") +} + +func init() { + proto.RegisterFile("babylon/finality/v1/finality.proto", fileDescriptor_ca5b87e52e3e6d02) +} + +var fileDescriptor_ca5b87e52e3e6d02 = []byte{ + // 424 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0x87, 0x9b, 0x6d, 0xb4, 0x9d, 0x17, 0x04, 0x98, 0x69, 0x2a, 0x08, 0x65, 0xa5, 0xa7, 0x1e, + 0x50, 0xb3, 0xb1, 0x09, 0x71, 0x25, 0xd2, 0xd0, 0x06, 0x07, 0x2a, 0x87, 0x13, 0x17, 0xcb, 0x76, + 0xdc, 0xd8, 0x6a, 0xb1, 0xad, 0xfc, 0xa9, 0x16, 0x3e, 0x05, 0x1f, 0x8b, 0xe3, 0x8e, 0x68, 0x87, + 0x0a, 0xb5, 0xdf, 0x03, 0xa1, 0x38, 0x69, 0x02, 0x27, 0x10, 0xbb, 0xbd, 0x7e, 0xfd, 0xd3, 0xfb, + 0x3c, 0x7a, 0x6d, 0x30, 0xa2, 0x84, 0x16, 0x0b, 0xad, 0xfc, 0x99, 0x54, 0x64, 0x21, 0xb3, 0xc2, + 0x5f, 0x9e, 0x36, 0xf5, 0xc4, 0x24, 0x3a, 0xd3, 0xf0, 0x71, 0x9d, 0x99, 0x34, 0xfd, 0xe5, 0xe9, + 0xd3, 0xc3, 0x58, 0xc7, 0xda, 0xde, 0xfb, 0x65, 0x55, 0x45, 0x47, 0x18, 0xb8, 0x57, 0x2a, 0xe2, + 0xd7, 0x3c, 0x0a, 0x16, 0x9a, 0xcd, 0xe1, 0x11, 0xe8, 0x0a, 0x2e, 0x63, 0x91, 0x0d, 0x9c, 0xa1, + 0x33, 0xde, 0x43, 0xf5, 0x09, 0x3e, 0x01, 0x7d, 0x62, 0x0c, 0x16, 0x24, 0x15, 0x83, 0x9d, 0xa1, + 0x33, 0x76, 0x51, 0x8f, 0x18, 0x73, 0x49, 0x52, 0x01, 0x9f, 0x81, 0xfd, 0x8a, 0xf3, 0x85, 0x47, + 0x83, 0xdd, 0xa1, 0x33, 0xee, 0xa3, 0xb6, 0x31, 0xfa, 0xb9, 0x0b, 0xfa, 0x17, 0x4b, 0x19, 0x71, + 0xc5, 0x38, 0x44, 0x60, 0x7f, 0x66, 0x30, 0xcd, 0x18, 0x36, 0x73, 0x0b, 0x70, 0x83, 0x57, 0xb7, + 0xab, 0xe3, 0x97, 0xb1, 0xcc, 0x44, 0x4e, 0x27, 0x4c, 0x7f, 0xf6, 0x6b, 0x75, 0x26, 0x88, 0x54, + 0xdb, 0x83, 0x9f, 0x15, 0x86, 0xa7, 0x93, 0xe0, 0x6a, 0x7a, 0x76, 0x7e, 0x32, 0xcd, 0xe9, 0x7b, + 0x5e, 0xa0, 0xde, 0xcc, 0x04, 0x19, 0x9b, 0xce, 0xe1, 0x73, 0xe0, 0xd2, 0x52, 0x1d, 0xd7, 0xde, + 0x3b, 0xd6, 0xfb, 0xc0, 0xf6, 0x2e, 0x2b, 0xf9, 0x10, 0xf4, 0x4d, 0x4e, 0x71, 0x42, 0x54, 0x25, + 0xe8, 0x06, 0xaf, 0x6f, 0x57, 0xc7, 0xe7, 0xff, 0x46, 0x0d, 0x99, 0x50, 0x3a, 0x49, 0xa6, 0x39, + 0x45, 0x44, 0x45, 0xa8, 0x67, 0xaa, 0x02, 0xbe, 0x00, 0x90, 0x11, 0xa5, 0x95, 0x64, 0x64, 0x81, + 0x9b, 0xdd, 0xec, 0xd9, 0xdd, 0x3c, 0x6c, 0x6e, 0xde, 0xd4, 0x4b, 0x1a, 0x81, 0xfb, 0x33, 0x9d, + 0xcc, 0xdb, 0xe0, 0x3d, 0x1b, 0x3c, 0x28, 0x9b, 0xdb, 0x8c, 0x02, 0x47, 0xed, 0xc4, 0xed, 0xd3, + 0xe1, 0x54, 0xc6, 0x83, 0xee, 0x7f, 0x4a, 0x5f, 0x7c, 0xf8, 0x18, 0x86, 0x32, 0x46, 0x87, 0xcd, + 0xdc, 0xb7, 0xf5, 0xd8, 0x50, 0xc6, 0x30, 0x02, 0x8f, 0xac, 0xd3, 0x1f, 0xa8, 0xde, 0x1d, 0x51, + 0x0f, 0xca, 0x91, 0xbf, 0x51, 0x82, 0x77, 0xdf, 0xd6, 0x9e, 0x73, 0xb3, 0xf6, 0x9c, 0x1f, 0x6b, + 0xcf, 0xf9, 0xba, 0xf1, 0x3a, 0x37, 0x1b, 0xaf, 0xf3, 0x7d, 0xe3, 0x75, 0x3e, 0x9d, 0xfc, 0x0d, + 0x70, 0xdd, 0x7e, 0x72, 0xcb, 0xa2, 0x5d, 0xfb, 0x69, 0xcf, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, + 0xac, 0x30, 0x4b, 0x66, 0x05, 0x03, 0x00, 0x00, +} + +func (m *IndexedBlock) 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 *IndexedBlock) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IndexedBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Finalized { + i-- + if m.Finalized { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.AppHash) > 0 { + i -= len(m.AppHash) + copy(dAtA[i:], m.AppHash) + i = encodeVarintFinality(dAtA, i, uint64(len(m.AppHash))) + i-- + dAtA[i] = 0x12 + } + if m.Height != 0 { + i = encodeVarintFinality(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Evidence) 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 *Evidence) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Evidence) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ForkFinalitySig != nil { + { + size := m.ForkFinalitySig.Size() + i -= size + if _, err := m.ForkFinalitySig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintFinality(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if m.CanonicalFinalitySig != nil { + { + size := m.CanonicalFinalitySig.Size() + i -= size + if _, err := m.CanonicalFinalitySig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintFinality(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.ForkAppHash) > 0 { + i -= len(m.ForkAppHash) + copy(dAtA[i:], m.ForkAppHash) + i = encodeVarintFinality(dAtA, i, uint64(len(m.ForkAppHash))) + i-- + dAtA[i] = 0x2a + } + if len(m.CanonicalAppHash) > 0 { + i -= len(m.CanonicalAppHash) + copy(dAtA[i:], m.CanonicalAppHash) + i = encodeVarintFinality(dAtA, i, uint64(len(m.CanonicalAppHash))) + i-- + dAtA[i] = 0x22 + } + if m.PubRand != nil { + { + size := m.PubRand.Size() + i -= size + if _, err := m.PubRand.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintFinality(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.BlockHeight != 0 { + i = encodeVarintFinality(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x10 + } + if m.FpBtcPk != nil { + { + size := m.FpBtcPk.Size() + i -= size + if _, err := m.FpBtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintFinality(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintFinality(dAtA []byte, offset int, v uint64) int { + offset -= sovFinality(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *IndexedBlock) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovFinality(uint64(m.Height)) + } + l = len(m.AppHash) + if l > 0 { + n += 1 + l + sovFinality(uint64(l)) + } + if m.Finalized { + n += 2 + } + return n +} + +func (m *Evidence) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FpBtcPk != nil { + l = m.FpBtcPk.Size() + n += 1 + l + sovFinality(uint64(l)) + } + if m.BlockHeight != 0 { + n += 1 + sovFinality(uint64(m.BlockHeight)) + } + if m.PubRand != nil { + l = m.PubRand.Size() + n += 1 + l + sovFinality(uint64(l)) + } + l = len(m.CanonicalAppHash) + if l > 0 { + n += 1 + l + sovFinality(uint64(l)) + } + l = len(m.ForkAppHash) + if l > 0 { + n += 1 + l + sovFinality(uint64(l)) + } + if m.CanonicalFinalitySig != nil { + l = m.CanonicalFinalitySig.Size() + n += 1 + l + sovFinality(uint64(l)) + } + if m.ForkFinalitySig != nil { + l = m.ForkFinalitySig.Size() + n += 1 + l + sovFinality(uint64(l)) + } + return n +} + +func sovFinality(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozFinality(x uint64) (n int) { + return sovFinality(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *IndexedBlock) 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 ErrIntOverflowFinality + } + 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: IndexedBlock: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IndexedBlock: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppHash = append(m.AppHash[:0], dAtA[iNdEx:postIndex]...) + if m.AppHash == nil { + m.AppHash = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Finalized", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Finalized = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipFinality(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFinality + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Evidence) 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 ErrIntOverflowFinality + } + 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: Evidence: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Evidence: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPk = &v + if err := m.FpBtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubRand", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.SchnorrPubRand + m.PubRand = &v + if err := m.PubRand.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CanonicalAppHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CanonicalAppHash = append(m.CanonicalAppHash[:0], dAtA[iNdEx:postIndex]...) + if m.CanonicalAppHash == nil { + m.CanonicalAppHash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForkAppHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ForkAppHash = append(m.ForkAppHash[:0], dAtA[iNdEx:postIndex]...) + if m.ForkAppHash == nil { + m.ForkAppHash = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CanonicalFinalitySig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.SchnorrEOTSSig + m.CanonicalFinalitySig = &v + if err := m.CanonicalFinalitySig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForkFinalitySig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFinality + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthFinality + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthFinality + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.SchnorrEOTSSig + m.ForkFinalitySig = &v + if err := m.ForkFinalitySig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFinality(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFinality + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipFinality(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, ErrIntOverflowFinality + } + 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, ErrIntOverflowFinality + } + 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, ErrIntOverflowFinality + } + 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, ErrInvalidLengthFinality + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupFinality + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthFinality + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthFinality = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowFinality = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupFinality = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/finality/types/genesis.go b/x/finality/types/genesis.go new file mode 100644 index 000000000..9d633ecd7 --- /dev/null +++ b/x/finality/types/genesis.go @@ -0,0 +1,14 @@ +package types + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + return gs.Params.Validate() +} diff --git a/x/finality/types/genesis.pb.go b/x/finality/types/genesis.pb.go new file mode 100644 index 000000000..59249a222 --- /dev/null +++ b/x/finality/types/genesis.pb.go @@ -0,0 +1,321 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/v1/genesis.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 + +// GenesisState defines the finality module's genesis state. +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_52dc577f74d797d1, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "babylon.finality.v1.GenesisState") +} + +func init() { proto.RegisterFile("babylon/finality/v1/genesis.proto", fileDescriptor_52dc577f74d797d1) } + +var fileDescriptor_52dc577f74d797d1 = []byte{ + // 200 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, 0x4f, 0xcb, 0xcc, 0x4b, 0xcc, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, 0xd4, + 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, + 0x2a, 0xd1, 0x83, 0x29, 0xd1, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, + 0x83, 0x58, 0x10, 0xa5, 0x52, 0x0a, 0xd8, 0x4c, 0x2b, 0x48, 0x2c, 0x4a, 0xcc, 0x85, 0x1a, 0xa6, + 0xe4, 0xc9, 0xc5, 0xe3, 0x0e, 0x31, 0x3d, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x92, 0x8b, 0x0d, + 0x22, 0x2f, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xad, 0x87, 0xc5, 0x36, 0xbd, 0x00, 0xb0, + 0x12, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x1a, 0x9c, 0xbc, 0x4e, 0x3c, 0x92, 0x63, + 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, + 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x20, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, + 0x3f, 0x57, 0x1f, 0x6a, 0x5c, 0x72, 0x46, 0x62, 0x66, 0x1e, 0x8c, 0xa3, 0x5f, 0x81, 0x70, 0x60, + 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x75, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x58, 0x11, 0xae, 0xb4, 0x0f, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) 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 = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: 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 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:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/finality/types/genesis_test.go b/x/finality/types/genesis_test.go new file mode 100644 index 000000000..97dbf25c2 --- /dev/null +++ b/x/finality/types/genesis_test.go @@ -0,0 +1,41 @@ +package types_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/finality/types" + "github.com/stretchr/testify/require" +) + +func TestGenesisState_Validate(t *testing.T) { + tests := []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + { + desc: "valid genesis state", + genState: &types.GenesisState{ + Params: types.Params{ + MinPubRand: 200, + }, + }, + valid: true, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/finality/types/keys.go b/x/finality/types/keys.go new file mode 100644 index 000000000..0a5ce2e40 --- /dev/null +++ b/x/finality/types/keys.go @@ -0,0 +1,28 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "finality" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_finality" +) + +var ( + BlockKey = []byte{0x01} // key prefix for blocks + VoteKey = []byte{0x02} // key prefix for votes + PubRandKey = []byte{0x03} // key prefix for public randomness + ParamsKey = []byte{0x04} // key prefix for the parameters + EvidenceKey = []byte{0x05} // key prefix for evidences + NextHeightToFinalizeKey = []byte{0x06} // key prefix for next height to finalise +) + +func KeyPrefix(p string) []byte { + return []byte(p) +} diff --git a/x/finality/types/mocked_keepers.go b/x/finality/types/mocked_keepers.go new file mode 100644 index 000000000..fc73bf710 --- /dev/null +++ b/x/finality/types/mocked_keepers.go @@ -0,0 +1,184 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/finality/types/expected_keepers.go + +// Package types is a generated GoMock package. +package types + +import ( + context "context" + reflect "reflect" + + types "github.com/babylonchain/babylon/x/btcstaking/types" + gomock "github.com/golang/mock/gomock" +) + +// MockBTCStakingKeeper is a mock of BTCStakingKeeper interface. +type MockBTCStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockBTCStakingKeeperMockRecorder +} + +// MockBTCStakingKeeperMockRecorder is the mock recorder for MockBTCStakingKeeper. +type MockBTCStakingKeeperMockRecorder struct { + mock *MockBTCStakingKeeper +} + +// NewMockBTCStakingKeeper creates a new mock instance. +func NewMockBTCStakingKeeper(ctrl *gomock.Controller) *MockBTCStakingKeeper { + mock := &MockBTCStakingKeeper{ctrl: ctrl} + mock.recorder = &MockBTCStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBTCStakingKeeper) EXPECT() *MockBTCStakingKeeperMockRecorder { + return m.recorder +} + +// GetBTCStakingActivatedHeight mocks base method. +func (m *MockBTCStakingKeeper) GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBTCStakingActivatedHeight", ctx) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBTCStakingActivatedHeight indicates an expected call of GetBTCStakingActivatedHeight. +func (mr *MockBTCStakingKeeperMockRecorder) GetBTCStakingActivatedHeight(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCStakingActivatedHeight", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetBTCStakingActivatedHeight), ctx) +} + +// GetFinalityProvider mocks base method. +func (m *MockBTCStakingKeeper) GetFinalityProvider(ctx context.Context, fpBTCPK []byte) (*types.FinalityProvider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFinalityProvider", ctx, fpBTCPK) + ret0, _ := ret[0].(*types.FinalityProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFinalityProvider indicates an expected call of GetFinalityProvider. +func (mr *MockBTCStakingKeeperMockRecorder) GetFinalityProvider(ctx, fpBTCPK interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFinalityProvider", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetFinalityProvider), ctx, fpBTCPK) +} + +// GetRewardDistCache mocks base method. +func (m *MockBTCStakingKeeper) GetRewardDistCache(ctx context.Context, height uint64) (*types.RewardDistCache, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRewardDistCache", ctx, height) + ret0, _ := ret[0].(*types.RewardDistCache) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRewardDistCache indicates an expected call of GetRewardDistCache. +func (mr *MockBTCStakingKeeperMockRecorder) GetRewardDistCache(ctx, height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRewardDistCache", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetRewardDistCache), ctx, height) +} + +// GetVotingPower mocks base method. +func (m *MockBTCStakingKeeper) GetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64) uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVotingPower", ctx, fpBTCPK, height) + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetVotingPower indicates an expected call of GetVotingPower. +func (mr *MockBTCStakingKeeperMockRecorder) GetVotingPower(ctx, fpBTCPK, height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVotingPower", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetVotingPower), ctx, fpBTCPK, height) +} + +// GetVotingPowerTable mocks base method. +func (m *MockBTCStakingKeeper) GetVotingPowerTable(ctx context.Context, height uint64) map[string]uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVotingPowerTable", ctx, height) + ret0, _ := ret[0].(map[string]uint64) + return ret0 +} + +// GetVotingPowerTable indicates an expected call of GetVotingPowerTable. +func (mr *MockBTCStakingKeeperMockRecorder) GetVotingPowerTable(ctx, height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVotingPowerTable", reflect.TypeOf((*MockBTCStakingKeeper)(nil).GetVotingPowerTable), ctx, height) +} + +// HasFinalityProvider mocks base method. +func (m *MockBTCStakingKeeper) HasFinalityProvider(ctx context.Context, fpBTCPK []byte) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasFinalityProvider", ctx, fpBTCPK) + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasFinalityProvider indicates an expected call of HasFinalityProvider. +func (mr *MockBTCStakingKeeperMockRecorder) HasFinalityProvider(ctx, fpBTCPK interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasFinalityProvider", reflect.TypeOf((*MockBTCStakingKeeper)(nil).HasFinalityProvider), ctx, fpBTCPK) +} + +// RemoveRewardDistCache mocks base method. +func (m *MockBTCStakingKeeper) RemoveRewardDistCache(ctx context.Context, height uint64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoveRewardDistCache", ctx, height) +} + +// RemoveRewardDistCache indicates an expected call of RemoveRewardDistCache. +func (mr *MockBTCStakingKeeperMockRecorder) RemoveRewardDistCache(ctx, height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveRewardDistCache", reflect.TypeOf((*MockBTCStakingKeeper)(nil).RemoveRewardDistCache), ctx, height) +} + +// SlashFinalityProvider mocks base method. +func (m *MockBTCStakingKeeper) SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SlashFinalityProvider", ctx, fpBTCPK) + ret0, _ := ret[0].(error) + return ret0 +} + +// SlashFinalityProvider indicates an expected call of SlashFinalityProvider. +func (mr *MockBTCStakingKeeperMockRecorder) SlashFinalityProvider(ctx, fpBTCPK interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlashFinalityProvider", reflect.TypeOf((*MockBTCStakingKeeper)(nil).SlashFinalityProvider), ctx, fpBTCPK) +} + +// MockIncentiveKeeper is a mock of IncentiveKeeper interface. +type MockIncentiveKeeper struct { + ctrl *gomock.Controller + recorder *MockIncentiveKeeperMockRecorder +} + +// MockIncentiveKeeperMockRecorder is the mock recorder for MockIncentiveKeeper. +type MockIncentiveKeeperMockRecorder struct { + mock *MockIncentiveKeeper +} + +// NewMockIncentiveKeeper creates a new mock instance. +func NewMockIncentiveKeeper(ctrl *gomock.Controller) *MockIncentiveKeeper { + mock := &MockIncentiveKeeper{ctrl: ctrl} + mock.recorder = &MockIncentiveKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIncentiveKeeper) EXPECT() *MockIncentiveKeeperMockRecorder { + return m.recorder +} + +// RewardBTCStaking mocks base method. +func (m *MockIncentiveKeeper) RewardBTCStaking(ctx context.Context, height uint64, rdc *types.RewardDistCache) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RewardBTCStaking", ctx, height, rdc) +} + +// RewardBTCStaking indicates an expected call of RewardBTCStaking. +func (mr *MockIncentiveKeeperMockRecorder) RewardBTCStaking(ctx, height, rdc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardBTCStaking", reflect.TypeOf((*MockIncentiveKeeper)(nil).RewardBTCStaking), ctx, height, rdc) +} diff --git a/x/finality/types/msg.go b/x/finality/types/msg.go new file mode 100644 index 000000000..af941426f --- /dev/null +++ b/x/finality/types/msg.go @@ -0,0 +1,86 @@ +package types + +import ( + "fmt" + + "github.com/babylonchain/babylon/crypto/eots" + bbn "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure that these message types implement the sdk.Msg interface +var ( + _ sdk.Msg = &MsgUpdateParams{} + _ sdk.Msg = &MsgAddFinalitySig{} + _ sdk.Msg = &MsgCommitPubRandList{} +) + +func NewMsgAddFinalitySig(signer string, sk *btcec.PrivateKey, sr *eots.PrivateRand, blockHeight uint64, blockHash []byte) (*MsgAddFinalitySig, error) { + msg := &MsgAddFinalitySig{ + Signer: signer, + FpBtcPk: bbn.NewBIP340PubKeyFromBTCPK(sk.PubKey()), + BlockHeight: blockHeight, + BlockAppHash: blockHash, + } + msgToSign := msg.MsgToSign() + sig, err := eots.Sign(sk, sr, msgToSign) + if err != nil { + return nil, err + } + msg.FinalitySig = bbn.NewSchnorrEOTSSigFromModNScalar(sig) + + return msg, nil +} + +func (m *MsgAddFinalitySig) MsgToSign() []byte { + return msgToSignForVote(m.BlockHeight, m.BlockAppHash) +} + +func (m *MsgAddFinalitySig) VerifyEOTSSig(pubRand *bbn.SchnorrPubRand) error { + msgToSign := m.MsgToSign() + pk, err := m.FpBtcPk.ToBTCPK() + if err != nil { + return err + } + + return eots.Verify(pk, pubRand.ToFieldVal(), msgToSign, m.FinalitySig.ToModNScalar()) +} + +// HashToSign returns a 32-byte hash of (start_height || pub_rand_list) +// The signature in MsgCommitPubRandList will be on this hash +func (m *MsgCommitPubRandList) HashToSign() ([]byte, error) { + hasher := tmhash.New() + if _, err := hasher.Write(sdk.Uint64ToBigEndian(m.StartHeight)); err != nil { + return nil, err + } + for _, pr := range m.PubRandList { + if _, err := hasher.Write(pr.MustMarshal()); err != nil { + return nil, err + } + } + return hasher.Sum(nil), nil +} + +func (m *MsgCommitPubRandList) VerifySig() error { + msgHash, err := m.HashToSign() + if err != nil { + return err + } + pk, err := m.FpBtcPk.ToBTCPK() + if err != nil { + return err + } + if m.Sig == nil { + return fmt.Errorf("empty signature") + } + schnorrSig, err := m.Sig.ToBTCSig() + if err != nil { + return err + } + if !schnorrSig.Verify(msgHash, pk) { + return fmt.Errorf("failed to verify signature") + } + return nil +} diff --git a/x/finality/types/msg_test.go b/x/finality/types/msg_test.go new file mode 100644 index 000000000..9dafd9d14 --- /dev/null +++ b/x/finality/types/msg_test.go @@ -0,0 +1,55 @@ +package types_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/crypto/eots" + "github.com/babylonchain/babylon/testutil/datagen" + bbn "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/finality/types" + "github.com/stretchr/testify/require" +) + +func FuzzMsgAddFinalitySig(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + sk, err := eots.KeyGen(r) + require.NoError(t, err) + sr, pr, err := eots.RandGen(r) + require.NoError(t, err) + blockHeight := datagen.RandomInt(r, 10) + blockHash := datagen.GenRandomByteArray(r, 32) + + signer := datagen.GenRandomAccount().Address + msg, err := types.NewMsgAddFinalitySig(signer, sk, sr, blockHeight, blockHash) + require.NoError(t, err) + + // verify msg's EOTS sig against the given public randomness + err = msg.VerifyEOTSSig(bbn.NewSchnorrPubRandFromFieldVal(pr)) + require.NoError(t, err) + }) +} + +func FuzzMsgCommitPubRandList(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + sk, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + startHeight := datagen.RandomInt(r, 10) + numPubRand := datagen.RandomInt(r, 100) + 1 + _, msg, err := datagen.GenRandomMsgCommitPubRandList(r, sk, startHeight, numPubRand) + require.NoError(t, err) + + // sanity checks, including verifying signature + err = msg.VerifySig() + require.NoError(t, err) + }) +} diff --git a/x/finality/types/params.go b/x/finality/types/params.go new file mode 100644 index 000000000..de592797b --- /dev/null +++ b/x/finality/types/params.go @@ -0,0 +1,47 @@ +package types + +import ( + "fmt" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "gopkg.in/yaml.v2" +) + +var _ paramtypes.ParamSet = (*Params)(nil) + +// ParamKeyTable the param key table for launch module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return Params{ + MinPubRand: 100, + } +} + +// ParamSetPairs get the params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{} +} + +func validateMinPubRand(minPubRand uint64) error { + if minPubRand == 0 { + return fmt.Errorf("min Pub Rand cannot be 0") + } + return nil +} + +// Validate validates the set of params +func (p Params) Validate() error { + if err := validateMinPubRand(p.MinPubRand); err != nil { + return err + } + return nil +} + +// String implements the Stringer interface. +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} diff --git a/x/finality/types/params.pb.go b/x/finality/types/params.pb.go new file mode 100644 index 000000000..fe81592df --- /dev/null +++ b/x/finality/types/params.pb.go @@ -0,0 +1,303 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/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 { + // min_pub_rand is the minimum number of public randomness each + // message should commit + MinPubRand uint64 `protobuf:"varint,1,opt,name=min_pub_rand,json=minPubRand,proto3" json:"min_pub_rand,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_25539c9a61c72ee9, []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) GetMinPubRand() uint64 { + if m != nil { + return m.MinPubRand + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "babylon.finality.v1.Params") +} + +func init() { proto.RegisterFile("babylon/finality/v1/params.proto", fileDescriptor_25539c9a61c72ee9) } + +var fileDescriptor_25539c9a61c72ee9 = []byte{ + // 192 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0xcb, 0xcc, 0x4b, 0xcc, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, 0xd4, + 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0xaa, + 0xd0, 0x83, 0xa9, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, + 0x58, 0x10, 0xa5, 0x4a, 0x06, 0x5c, 0x6c, 0x01, 0x60, 0xad, 0x42, 0x0a, 0x5c, 0x3c, 0xb9, 0x99, + 0x79, 0xf1, 0x05, 0xa5, 0x49, 0xf1, 0x45, 0x89, 0x79, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, + 0x41, 0x5c, 0xb9, 0x99, 0x79, 0x01, 0xa5, 0x49, 0x41, 0x89, 0x79, 0x29, 0x56, 0x2c, 0x33, 0x16, + 0xc8, 0x33, 0x38, 0x79, 0x9d, 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, 0x41, + 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0xd4, 0x05, 0xc9, 0x19, 0x89, + 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x05, 0xc2, 0xc9, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, + 0x47, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x3d, 0x78, 0xb7, 0xd3, 0x00, 0x00, 0x00, +} + +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.MinPubRand != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MinPubRand)) + 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.MinPubRand != 0 { + n += 1 + sovParams(uint64(m.MinPubRand)) + } + 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 MinPubRand", wireType) + } + m.MinPubRand = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinPubRand |= uint64(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/finality/types/query.go b/x/finality/types/query.go new file mode 100644 index 000000000..f6dc32702 --- /dev/null +++ b/x/finality/types/query.go @@ -0,0 +1,20 @@ +package types + +import ( + "fmt" +) + +// NewQueriedBlockStatus takes a human-readable queried block status format and returns our custom enum. +// Options: NonFinalized | Finalized | Any +func NewQueriedBlockStatus(status string) (QueriedBlockStatus, error) { + if status == "NonFinalized" { + return QueriedBlockStatus_NON_FINALIZED, nil + } + if status == "Finalized" { + return QueriedBlockStatus_FINALIZED, nil + } + if status == "Any" { + return QueriedBlockStatus_ANY, nil + } + return QueriedBlockStatus_NON_FINALIZED, fmt.Errorf("invalid queried block status %s", status) +} diff --git a/x/finality/types/query.pb.go b/x/finality/types/query.pb.go new file mode 100644 index 000000000..a4b12e2ea --- /dev/null +++ b/x/finality/types/query.pb.go @@ -0,0 +1,3375 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/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" + 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. +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 + +// QueriedBlockStatus is the status of blocks that the querier wants to query. +type QueriedBlockStatus int32 + +const ( + // NON_FINALIZED means the block is not finalised + QueriedBlockStatus_NON_FINALIZED QueriedBlockStatus = 0 + // FINALIZED means the block is finalized + QueriedBlockStatus_FINALIZED QueriedBlockStatus = 1 + // ANY means the block can be in any status + QueriedBlockStatus_ANY QueriedBlockStatus = 2 +) + +var QueriedBlockStatus_name = map[int32]string{ + 0: "NON_FINALIZED", + 1: "FINALIZED", + 2: "ANY", +} + +var QueriedBlockStatus_value = map[string]int32{ + "NON_FINALIZED": 0, + "FINALIZED": 1, + "ANY": 2, +} + +func (x QueriedBlockStatus) String() string { + return proto.EnumName(QueriedBlockStatus_name, int32(x)) +} + +func (QueriedBlockStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{0} +} + +// QueryParamsRequest is 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_32bddab77af6fdae, []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 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_32bddab77af6fdae, []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{} +} + +// QueryListPublicRandomnessRequest is the request type for the +// Query/ListPublicRandomness RPC method. +type QueryListPublicRandomnessRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the finality provider + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListPublicRandomnessRequest) Reset() { *m = QueryListPublicRandomnessRequest{} } +func (m *QueryListPublicRandomnessRequest) String() string { return proto.CompactTextString(m) } +func (*QueryListPublicRandomnessRequest) ProtoMessage() {} +func (*QueryListPublicRandomnessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{2} +} +func (m *QueryListPublicRandomnessRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListPublicRandomnessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListPublicRandomnessRequest.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 *QueryListPublicRandomnessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPublicRandomnessRequest.Merge(m, src) +} +func (m *QueryListPublicRandomnessRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryListPublicRandomnessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPublicRandomnessRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListPublicRandomnessRequest proto.InternalMessageInfo + +func (m *QueryListPublicRandomnessRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +func (m *QueryListPublicRandomnessRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryListPublicRandomnessResponse is the response type for the +// Query/ListPublicRandomness RPC method. +type QueryListPublicRandomnessResponse struct { + // pub_rand_map is the map where the key is the height and the value + // is the public randomness at this height for the given finality provider + PubRandMap map[uint64]*github_com_babylonchain_babylon_types.SchnorrPubRand `protobuf:"bytes,1,rep,name=pub_rand_map,json=pubRandMap,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrPubRand" json:"pub_rand_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListPublicRandomnessResponse) Reset() { *m = QueryListPublicRandomnessResponse{} } +func (m *QueryListPublicRandomnessResponse) String() string { return proto.CompactTextString(m) } +func (*QueryListPublicRandomnessResponse) ProtoMessage() {} +func (*QueryListPublicRandomnessResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{3} +} +func (m *QueryListPublicRandomnessResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListPublicRandomnessResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListPublicRandomnessResponse.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 *QueryListPublicRandomnessResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPublicRandomnessResponse.Merge(m, src) +} +func (m *QueryListPublicRandomnessResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryListPublicRandomnessResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPublicRandomnessResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListPublicRandomnessResponse proto.InternalMessageInfo + +func (m *QueryListPublicRandomnessResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryBlockRequest is the request type for the +// Query/Block RPC method. +type QueryBlockRequest struct { + // height is the height of the Babylon block + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryBlockRequest) Reset() { *m = QueryBlockRequest{} } +func (m *QueryBlockRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBlockRequest) ProtoMessage() {} +func (*QueryBlockRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{4} +} +func (m *QueryBlockRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBlockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBlockRequest.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 *QueryBlockRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBlockRequest.Merge(m, src) +} +func (m *QueryBlockRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBlockRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBlockRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBlockRequest proto.InternalMessageInfo + +func (m *QueryBlockRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryBlockResponse is the response type for the +// Query/Block RPC method. +type QueryBlockResponse struct { + // block is the Babylon at the given height + Block *IndexedBlock `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` +} + +func (m *QueryBlockResponse) Reset() { *m = QueryBlockResponse{} } +func (m *QueryBlockResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBlockResponse) ProtoMessage() {} +func (*QueryBlockResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{5} +} +func (m *QueryBlockResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBlockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBlockResponse.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 *QueryBlockResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBlockResponse.Merge(m, src) +} +func (m *QueryBlockResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBlockResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBlockResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBlockResponse proto.InternalMessageInfo + +func (m *QueryBlockResponse) GetBlock() *IndexedBlock { + if m != nil { + return m.Block + } + return nil +} + +// QueryListBlocksRequest is the request type for the +// Query/ListBlocks RPC method. +type QueryListBlocksRequest struct { + // status indicates the status of blocks that the querier wants to query + Status QueriedBlockStatus `protobuf:"varint,1,opt,name=status,proto3,enum=babylon.finality.v1.QueriedBlockStatus" json:"status,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListBlocksRequest) Reset() { *m = QueryListBlocksRequest{} } +func (m *QueryListBlocksRequest) String() string { return proto.CompactTextString(m) } +func (*QueryListBlocksRequest) ProtoMessage() {} +func (*QueryListBlocksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{6} +} +func (m *QueryListBlocksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListBlocksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListBlocksRequest.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 *QueryListBlocksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListBlocksRequest.Merge(m, src) +} +func (m *QueryListBlocksRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryListBlocksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListBlocksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListBlocksRequest proto.InternalMessageInfo + +func (m *QueryListBlocksRequest) GetStatus() QueriedBlockStatus { + if m != nil { + return m.Status + } + return QueriedBlockStatus_NON_FINALIZED +} + +func (m *QueryListBlocksRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryListBlocksResponse is the response type for the +// Query/ListBlocks RPC method. +type QueryListBlocksResponse struct { + // blocks is the list of blocks at the given status + Blocks []*IndexedBlock `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListBlocksResponse) Reset() { *m = QueryListBlocksResponse{} } +func (m *QueryListBlocksResponse) String() string { return proto.CompactTextString(m) } +func (*QueryListBlocksResponse) ProtoMessage() {} +func (*QueryListBlocksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{7} +} +func (m *QueryListBlocksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListBlocksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListBlocksResponse.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 *QueryListBlocksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListBlocksResponse.Merge(m, src) +} +func (m *QueryListBlocksResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryListBlocksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListBlocksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListBlocksResponse proto.InternalMessageInfo + +func (m *QueryListBlocksResponse) GetBlocks() []*IndexedBlock { + if m != nil { + return m.Blocks + } + return nil +} + +func (m *QueryListBlocksResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryVotesAtHeightRequest is the request type for the +// Query/VotesAtHeight RPC method. +type QueryVotesAtHeightRequest struct { + // height defines at which height to query the finality providers. + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryVotesAtHeightRequest) Reset() { *m = QueryVotesAtHeightRequest{} } +func (m *QueryVotesAtHeightRequest) String() string { return proto.CompactTextString(m) } +func (*QueryVotesAtHeightRequest) ProtoMessage() {} +func (*QueryVotesAtHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{8} +} +func (m *QueryVotesAtHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVotesAtHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVotesAtHeightRequest.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 *QueryVotesAtHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVotesAtHeightRequest.Merge(m, src) +} +func (m *QueryVotesAtHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryVotesAtHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVotesAtHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVotesAtHeightRequest proto.InternalMessageInfo + +func (m *QueryVotesAtHeightRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryVotesAtHeightResponse is the response type for the +// Query/VotesAtHeight RPC method. +type QueryVotesAtHeightResponse struct { + // btc_pk is the Bitcoin secp256k1 PK of finality providers who have signed the block at given height. + // the PK follows encoding in BIP-340 spec + BtcPks []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,1,rep,name=btc_pks,json=btcPks,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pks,omitempty"` +} + +func (m *QueryVotesAtHeightResponse) Reset() { *m = QueryVotesAtHeightResponse{} } +func (m *QueryVotesAtHeightResponse) String() string { return proto.CompactTextString(m) } +func (*QueryVotesAtHeightResponse) ProtoMessage() {} +func (*QueryVotesAtHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{9} +} +func (m *QueryVotesAtHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVotesAtHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVotesAtHeightResponse.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 *QueryVotesAtHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVotesAtHeightResponse.Merge(m, src) +} +func (m *QueryVotesAtHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryVotesAtHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVotesAtHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVotesAtHeightResponse proto.InternalMessageInfo + +// QueryEvidenceRequest is the request type for the +// Query/Evidence RPC method. +type QueryEvidenceRequest struct { + // fp_btc_pk_hex is the hex str of Bitcoin secp256k1 PK + // (in BIP340 format) of the finality provider + FpBtcPkHex string `protobuf:"bytes,1,opt,name=fp_btc_pk_hex,json=fpBtcPkHex,proto3" json:"fp_btc_pk_hex,omitempty"` +} + +func (m *QueryEvidenceRequest) Reset() { *m = QueryEvidenceRequest{} } +func (m *QueryEvidenceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryEvidenceRequest) ProtoMessage() {} +func (*QueryEvidenceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{10} +} +func (m *QueryEvidenceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEvidenceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEvidenceRequest.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 *QueryEvidenceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEvidenceRequest.Merge(m, src) +} +func (m *QueryEvidenceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryEvidenceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEvidenceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEvidenceRequest proto.InternalMessageInfo + +func (m *QueryEvidenceRequest) GetFpBtcPkHex() string { + if m != nil { + return m.FpBtcPkHex + } + return "" +} + +// QueryEvidenceResponse is the response type for the +// Query/Evidence RPC method. +type QueryEvidenceResponse struct { + Evidence *Evidence `protobuf:"bytes,1,opt,name=evidence,proto3" json:"evidence,omitempty"` +} + +func (m *QueryEvidenceResponse) Reset() { *m = QueryEvidenceResponse{} } +func (m *QueryEvidenceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryEvidenceResponse) ProtoMessage() {} +func (*QueryEvidenceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{11} +} +func (m *QueryEvidenceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEvidenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEvidenceResponse.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 *QueryEvidenceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEvidenceResponse.Merge(m, src) +} +func (m *QueryEvidenceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryEvidenceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEvidenceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEvidenceResponse proto.InternalMessageInfo + +func (m *QueryEvidenceResponse) GetEvidence() *Evidence { + if m != nil { + return m.Evidence + } + return nil +} + +// QueryListEvidencesRequest is the request type for the +// Query/ListEvidences RPC method. +type QueryListEvidencesRequest struct { + // start_height is the starting height that the querier specifies + // such that the RPC will only return evidences since this height + StartHeight uint64 `protobuf:"varint,1,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListEvidencesRequest) Reset() { *m = QueryListEvidencesRequest{} } +func (m *QueryListEvidencesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryListEvidencesRequest) ProtoMessage() {} +func (*QueryListEvidencesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{12} +} +func (m *QueryListEvidencesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListEvidencesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListEvidencesRequest.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 *QueryListEvidencesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListEvidencesRequest.Merge(m, src) +} +func (m *QueryListEvidencesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryListEvidencesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListEvidencesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListEvidencesRequest proto.InternalMessageInfo + +func (m *QueryListEvidencesRequest) GetStartHeight() uint64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +func (m *QueryListEvidencesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryListEvidencesResponse is the response type for the +// Query/ListEvidences RPC method. +type QueryListEvidencesResponse struct { + // blocks is the list of evidences + Evidences []*Evidence `protobuf:"bytes,1,rep,name=evidences,proto3" json:"evidences,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryListEvidencesResponse) Reset() { *m = QueryListEvidencesResponse{} } +func (m *QueryListEvidencesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryListEvidencesResponse) ProtoMessage() {} +func (*QueryListEvidencesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_32bddab77af6fdae, []int{13} +} +func (m *QueryListEvidencesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListEvidencesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListEvidencesResponse.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 *QueryListEvidencesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListEvidencesResponse.Merge(m, src) +} +func (m *QueryListEvidencesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryListEvidencesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListEvidencesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListEvidencesResponse proto.InternalMessageInfo + +func (m *QueryListEvidencesResponse) GetEvidences() []*Evidence { + if m != nil { + return m.Evidences + } + return nil +} + +func (m *QueryListEvidencesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterEnum("babylon.finality.v1.QueriedBlockStatus", QueriedBlockStatus_name, QueriedBlockStatus_value) + proto.RegisterType((*QueryParamsRequest)(nil), "babylon.finality.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "babylon.finality.v1.QueryParamsResponse") + proto.RegisterType((*QueryListPublicRandomnessRequest)(nil), "babylon.finality.v1.QueryListPublicRandomnessRequest") + proto.RegisterType((*QueryListPublicRandomnessResponse)(nil), "babylon.finality.v1.QueryListPublicRandomnessResponse") + proto.RegisterMapType((map[uint64]*github_com_babylonchain_babylon_types.SchnorrPubRand)(nil), "babylon.finality.v1.QueryListPublicRandomnessResponse.PubRandMapEntry") + proto.RegisterType((*QueryBlockRequest)(nil), "babylon.finality.v1.QueryBlockRequest") + proto.RegisterType((*QueryBlockResponse)(nil), "babylon.finality.v1.QueryBlockResponse") + proto.RegisterType((*QueryListBlocksRequest)(nil), "babylon.finality.v1.QueryListBlocksRequest") + proto.RegisterType((*QueryListBlocksResponse)(nil), "babylon.finality.v1.QueryListBlocksResponse") + proto.RegisterType((*QueryVotesAtHeightRequest)(nil), "babylon.finality.v1.QueryVotesAtHeightRequest") + proto.RegisterType((*QueryVotesAtHeightResponse)(nil), "babylon.finality.v1.QueryVotesAtHeightResponse") + proto.RegisterType((*QueryEvidenceRequest)(nil), "babylon.finality.v1.QueryEvidenceRequest") + proto.RegisterType((*QueryEvidenceResponse)(nil), "babylon.finality.v1.QueryEvidenceResponse") + proto.RegisterType((*QueryListEvidencesRequest)(nil), "babylon.finality.v1.QueryListEvidencesRequest") + proto.RegisterType((*QueryListEvidencesResponse)(nil), "babylon.finality.v1.QueryListEvidencesResponse") +} + +func init() { proto.RegisterFile("babylon/finality/v1/query.proto", fileDescriptor_32bddab77af6fdae) } + +var fileDescriptor_32bddab77af6fdae = []byte{ + // 1028 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0x38, 0x8d, 0x9b, 0xbc, 0xc4, 0x90, 0x4e, 0x4d, 0x09, 0x2e, 0x75, 0x9c, 0x2d, 0x24, + 0x21, 0xa9, 0x76, 0x1b, 0xa7, 0x94, 0x16, 0x84, 0x4a, 0x2c, 0x12, 0x12, 0x68, 0x5d, 0xb3, 0x95, + 0x2a, 0xd1, 0x8b, 0x35, 0x6b, 0x4f, 0xec, 0x55, 0xec, 0x9d, 0xed, 0xee, 0xac, 0x15, 0xab, 0xaa, + 0x84, 0x38, 0xf4, 0x04, 0x12, 0x12, 0x17, 0x2e, 0x3d, 0xd0, 0x2b, 0xff, 0x48, 0x8f, 0x91, 0xb8, + 0xa0, 0x4a, 0x44, 0x28, 0xe1, 0xc6, 0x3f, 0x81, 0x76, 0x66, 0xd6, 0x8e, 0xc3, 0xfa, 0x47, 0xab, + 0xdc, 0xbc, 0x33, 0xef, 0xc7, 0xf7, 0xbe, 0xf7, 0xe6, 0x7b, 0x86, 0x79, 0x8b, 0x58, 0xed, 0x06, + 0x73, 0x8c, 0x5d, 0xdb, 0x21, 0x0d, 0x9b, 0xb7, 0x8d, 0xd6, 0x9a, 0xf1, 0x38, 0xa0, 0x5e, 0x5b, + 0x77, 0x3d, 0xc6, 0x19, 0xbe, 0xa8, 0x0c, 0xf4, 0xc8, 0x40, 0x6f, 0xad, 0x65, 0xd2, 0x35, 0x56, + 0x63, 0xe2, 0xde, 0x08, 0x7f, 0x49, 0xd3, 0xcc, 0xfb, 0x35, 0xc6, 0x6a, 0x0d, 0x6a, 0x10, 0xd7, + 0x36, 0x88, 0xe3, 0x30, 0x4e, 0xb8, 0xcd, 0x1c, 0x5f, 0xdd, 0xae, 0x54, 0x98, 0xdf, 0x64, 0xbe, + 0x61, 0x11, 0x9f, 0xca, 0x0c, 0x46, 0x6b, 0xcd, 0xa2, 0x9c, 0xac, 0x19, 0x2e, 0xa9, 0xd9, 0x8e, + 0x30, 0x56, 0xb6, 0xb9, 0x38, 0x54, 0x2e, 0xf1, 0x48, 0x33, 0x8a, 0xa6, 0xc5, 0x59, 0x74, 0x20, + 0x0a, 0x1b, 0x2d, 0x0d, 0xf8, 0xdb, 0x30, 0x4f, 0x49, 0x38, 0x9a, 0xf4, 0x71, 0x40, 0x7d, 0xae, + 0x95, 0xe0, 0x62, 0xcf, 0xa9, 0xef, 0x32, 0xc7, 0xa7, 0xf8, 0x36, 0x24, 0x65, 0x82, 0x39, 0x94, + 0x43, 0xcb, 0xd3, 0xf9, 0xcb, 0x7a, 0x4c, 0xe1, 0xba, 0x74, 0x2a, 0x9c, 0x7b, 0x79, 0x38, 0x3f, + 0x66, 0x2a, 0x07, 0xed, 0x27, 0x04, 0x39, 0x11, 0xf2, 0xae, 0xed, 0xf3, 0x52, 0x60, 0x35, 0xec, + 0x8a, 0x49, 0x9c, 0x2a, 0x6b, 0x3a, 0xd4, 0x8f, 0xd2, 0xe2, 0x05, 0x48, 0xed, 0xba, 0x65, 0x8b, + 0x57, 0xca, 0xee, 0x5e, 0xb9, 0x4e, 0xf7, 0x45, 0x9a, 0x29, 0x13, 0x76, 0xdd, 0x02, 0xaf, 0x94, + 0xf6, 0xb6, 0xe9, 0x3e, 0xde, 0x02, 0xe8, 0x32, 0x31, 0x97, 0x10, 0x30, 0x16, 0x75, 0x49, 0x9b, + 0x1e, 0xd2, 0xa6, 0xcb, 0xc6, 0x28, 0xda, 0xf4, 0x12, 0xa9, 0x51, 0x15, 0xde, 0x3c, 0xe1, 0xa9, + 0x1d, 0x24, 0x60, 0x61, 0x00, 0x1e, 0x55, 0xf0, 0x0b, 0x04, 0x33, 0x6e, 0x60, 0x95, 0x3d, 0xe2, + 0x54, 0xcb, 0x4d, 0xe2, 0xce, 0xa1, 0xdc, 0xf8, 0xf2, 0x74, 0x7e, 0x2b, 0xb6, 0xee, 0xa1, 0xe1, + 0xf4, 0x52, 0x60, 0x85, 0xa7, 0xf7, 0x88, 0xbb, 0xe9, 0x70, 0xaf, 0x5d, 0xb8, 0xf5, 0xea, 0x70, + 0xfe, 0x46, 0xcd, 0xe6, 0xf5, 0xc0, 0xd2, 0x2b, 0xac, 0x69, 0xa8, 0xa8, 0x95, 0x3a, 0xb1, 0x9d, + 0xe8, 0xc3, 0xe0, 0x6d, 0x97, 0xfa, 0xfa, 0x83, 0x4a, 0xdd, 0x61, 0x9e, 0xa7, 0x22, 0x98, 0xe0, + 0x76, 0x42, 0xe1, 0xaf, 0x62, 0x28, 0x59, 0x1a, 0x4a, 0x89, 0x84, 0x74, 0x92, 0x93, 0xcc, 0xe7, + 0xf0, 0xf6, 0x29, 0x84, 0x78, 0x16, 0xc6, 0xf7, 0x68, 0x5b, 0xf4, 0xe1, 0x9c, 0x19, 0xfe, 0xc4, + 0x69, 0x98, 0x68, 0x91, 0x46, 0x40, 0x45, 0xa2, 0x19, 0x53, 0x7e, 0x7c, 0x9a, 0xb8, 0x85, 0xb4, + 0x55, 0xb8, 0x20, 0x28, 0x28, 0x34, 0x58, 0x65, 0x2f, 0x6a, 0xe9, 0x25, 0x48, 0xd6, 0xa9, 0x5d, + 0xab, 0x73, 0x15, 0x43, 0x7d, 0x69, 0xf7, 0xd4, 0xdc, 0x29, 0x63, 0xc5, 0xf7, 0x27, 0x30, 0x61, + 0x85, 0x07, 0x6a, 0xbe, 0x16, 0x62, 0x79, 0xde, 0x71, 0xaa, 0x74, 0x9f, 0x56, 0xa5, 0xa7, 0xb4, + 0xd7, 0x7e, 0x43, 0x70, 0xa9, 0xc3, 0xbf, 0xb8, 0xe9, 0x0c, 0xd5, 0x1d, 0x48, 0xfa, 0x9c, 0xf0, + 0x40, 0x0e, 0xed, 0x5b, 0xf9, 0xa5, 0xbe, 0xcd, 0xb3, 0x55, 0xd0, 0x07, 0xc2, 0xdc, 0x54, 0x6e, + 0x67, 0x36, 0x72, 0xcf, 0x11, 0xbc, 0xfb, 0x3f, 0x8c, 0xdd, 0x97, 0x25, 0x0a, 0xf1, 0xd5, 0x84, + 0x8d, 0x50, 0xb9, 0x72, 0x38, 0xb3, 0xf6, 0x6b, 0xeb, 0xf0, 0x9e, 0x80, 0xf7, 0x90, 0x71, 0xea, + 0x6f, 0xf0, 0x6d, 0xd1, 0xa8, 0x61, 0x7d, 0x6c, 0x42, 0x26, 0xce, 0x49, 0x95, 0x75, 0x1f, 0xce, + 0xcb, 0xd7, 0x2c, 0xeb, 0x9a, 0x29, 0xdc, 0x7c, 0x75, 0x38, 0x9f, 0x1f, 0x6d, 0xe2, 0x0b, 0x3b, + 0xa5, 0xf5, 0x1b, 0xd7, 0x4b, 0x81, 0xf5, 0x0d, 0x6d, 0x9b, 0x49, 0x2b, 0x14, 0x00, 0x5f, 0xbb, + 0x0d, 0x69, 0x91, 0x6e, 0xb3, 0x65, 0x57, 0xa9, 0x53, 0xa1, 0xa3, 0x2b, 0x87, 0x66, 0xc2, 0x3b, + 0xa7, 0x5c, 0x3b, 0xdc, 0x4f, 0x52, 0x75, 0xa6, 0xe6, 0xee, 0x4a, 0x2c, 0xfb, 0x1d, 0xc7, 0x8e, + 0xb9, 0xf6, 0x0c, 0x29, 0xce, 0xc2, 0x96, 0x46, 0xf7, 0x27, 0xe4, 0x6c, 0xc6, 0xe7, 0xc4, 0xe3, + 0xe5, 0x1e, 0xe6, 0xa6, 0xc5, 0x99, 0x24, 0xea, 0xcc, 0x66, 0xeb, 0x05, 0x52, 0x7d, 0x38, 0x05, + 0x44, 0x95, 0xf8, 0x19, 0x4c, 0x45, 0x98, 0xa3, 0x09, 0x1b, 0x52, 0x63, 0xd7, 0xfe, 0xcc, 0x06, + 0x6c, 0xe5, 0x8e, 0x7c, 0xf3, 0xbd, 0xcf, 0x0c, 0x5f, 0x80, 0x54, 0xf1, 0x7e, 0xb1, 0xbc, 0xb5, + 0x53, 0xdc, 0xb8, 0xbb, 0xf3, 0x68, 0xf3, 0xcb, 0xd9, 0x31, 0x9c, 0x82, 0xa9, 0xee, 0x27, 0xc2, + 0xe7, 0x61, 0x7c, 0xa3, 0xf8, 0xdd, 0x6c, 0x22, 0xff, 0xef, 0x24, 0x4c, 0x88, 0x2a, 0xf1, 0xf7, + 0x08, 0x92, 0x72, 0xcf, 0xe0, 0xfe, 0xef, 0xb9, 0x77, 0xa9, 0x65, 0x96, 0x87, 0x1b, 0x4a, 0xd0, + 0xda, 0xd5, 0x1f, 0xfe, 0xf8, 0xe7, 0x97, 0xc4, 0x15, 0x7c, 0xd9, 0xe8, 0xbf, 0x63, 0xf1, 0x5f, + 0x08, 0xd2, 0x71, 0x6a, 0x8f, 0x3f, 0x7e, 0xdd, 0xed, 0x20, 0xe1, 0xdd, 0x7c, 0xb3, 0xa5, 0xa2, + 0x3d, 0x14, 0x60, 0x4b, 0xb8, 0x68, 0x0c, 0x5a, 0xf7, 0x65, 0xd7, 0x63, 0x61, 0x47, 0x3d, 0xdf, + 0x78, 0xd2, 0xf3, 0x52, 0x9e, 0x1a, 0xae, 0x88, 0x2c, 0x76, 0x9c, 0x0c, 0x5d, 0x6e, 0xd8, 0x3e, + 0xc7, 0xcf, 0x10, 0x4c, 0x88, 0x3e, 0xe1, 0xc5, 0xfe, 0xc8, 0x4e, 0x6a, 0x7d, 0x66, 0x69, 0xa8, + 0x9d, 0x82, 0x7c, 0x4d, 0x40, 0x5e, 0xc4, 0x1f, 0xc4, 0x42, 0x96, 0xba, 0x66, 0x3c, 0x91, 0xaf, + 0xe6, 0x29, 0xfe, 0x11, 0x01, 0x74, 0x25, 0x13, 0xaf, 0x0e, 0xe6, 0xa9, 0x47, 0xfc, 0x33, 0xd7, + 0x46, 0x33, 0x1e, 0xa9, 0xef, 0x4a, 0x6f, 0x9f, 0x23, 0x48, 0xf5, 0xa8, 0x1d, 0xd6, 0xfb, 0x27, + 0x89, 0xd3, 0xd2, 0x8c, 0x31, 0xb2, 0xbd, 0xc2, 0xb5, 0x2a, 0x70, 0x7d, 0x88, 0xaf, 0xc6, 0xe2, + 0x6a, 0x85, 0x3e, 0x5d, 0xba, 0x7e, 0x47, 0x30, 0x19, 0x3d, 0x63, 0xfc, 0x51, 0xff, 0x54, 0xa7, + 0x24, 0x34, 0xb3, 0x32, 0x8a, 0xa9, 0x02, 0xb4, 0x2d, 0x00, 0x15, 0xf0, 0x17, 0x6f, 0x3a, 0x73, + 0x91, 0xba, 0xe0, 0x5f, 0x11, 0xa4, 0x7a, 0x34, 0x6b, 0x10, 0x9b, 0x71, 0x2a, 0x3b, 0x88, 0xcd, + 0x58, 0x31, 0xd4, 0x16, 0x05, 0xf8, 0x1c, 0xce, 0xc6, 0x82, 0xef, 0xe8, 0x5e, 0xe1, 0xeb, 0x97, + 0x47, 0x59, 0x74, 0x70, 0x94, 0x45, 0x7f, 0x1f, 0x65, 0xd1, 0xcf, 0xc7, 0xd9, 0xb1, 0x83, 0xe3, + 0xec, 0xd8, 0x9f, 0xc7, 0xd9, 0xb1, 0x47, 0xd7, 0x87, 0x6d, 0xb0, 0xfd, 0x6e, 0x48, 0xb1, 0xcc, + 0xac, 0xa4, 0xf8, 0xb7, 0xbd, 0xfe, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x08, 0x51, 0x06, + 0x4b, 0x0c, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// 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 { + // Parameters queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // ListPublicRandomness is a range query for public randomness of a given finality provider + ListPublicRandomness(ctx context.Context, in *QueryListPublicRandomnessRequest, opts ...grpc.CallOption) (*QueryListPublicRandomnessResponse, error) + // Block queries a block at a given height + Block(ctx context.Context, in *QueryBlockRequest, opts ...grpc.CallOption) (*QueryBlockResponse, error) + // ListBlocks is a range query for blocks at a given status + ListBlocks(ctx context.Context, in *QueryListBlocksRequest, opts ...grpc.CallOption) (*QueryListBlocksResponse, error) + // VotesAtHeight queries finality providers who have signed the block at given height. + VotesAtHeight(ctx context.Context, in *QueryVotesAtHeightRequest, opts ...grpc.CallOption) (*QueryVotesAtHeightResponse, error) + // Evidence queries the first evidence which can be used for extracting the BTC SK + Evidence(ctx context.Context, in *QueryEvidenceRequest, opts ...grpc.CallOption) (*QueryEvidenceResponse, error) + // ListEvidences queries is a range query for evidences + ListEvidences(ctx context.Context, in *QueryListEvidencesRequest, opts ...grpc.CallOption) (*QueryListEvidencesResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +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.finality.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ListPublicRandomness(ctx context.Context, in *QueryListPublicRandomnessRequest, opts ...grpc.CallOption) (*QueryListPublicRandomnessResponse, error) { + out := new(QueryListPublicRandomnessResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/ListPublicRandomness", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Block(ctx context.Context, in *QueryBlockRequest, opts ...grpc.CallOption) (*QueryBlockResponse, error) { + out := new(QueryBlockResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/Block", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ListBlocks(ctx context.Context, in *QueryListBlocksRequest, opts ...grpc.CallOption) (*QueryListBlocksResponse, error) { + out := new(QueryListBlocksResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/ListBlocks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) VotesAtHeight(ctx context.Context, in *QueryVotesAtHeightRequest, opts ...grpc.CallOption) (*QueryVotesAtHeightResponse, error) { + out := new(QueryVotesAtHeightResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/VotesAtHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Evidence(ctx context.Context, in *QueryEvidenceRequest, opts ...grpc.CallOption) (*QueryEvidenceResponse, error) { + out := new(QueryEvidenceResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/Evidence", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ListEvidences(ctx context.Context, in *QueryListEvidencesRequest, opts ...grpc.CallOption) (*QueryListEvidencesResponse, error) { + out := new(QueryListEvidencesResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Query/ListEvidences", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Parameters queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // ListPublicRandomness is a range query for public randomness of a given finality provider + ListPublicRandomness(context.Context, *QueryListPublicRandomnessRequest) (*QueryListPublicRandomnessResponse, error) + // Block queries a block at a given height + Block(context.Context, *QueryBlockRequest) (*QueryBlockResponse, error) + // ListBlocks is a range query for blocks at a given status + ListBlocks(context.Context, *QueryListBlocksRequest) (*QueryListBlocksResponse, error) + // VotesAtHeight queries finality providers who have signed the block at given height. + VotesAtHeight(context.Context, *QueryVotesAtHeightRequest) (*QueryVotesAtHeightResponse, error) + // Evidence queries the first evidence which can be used for extracting the BTC SK + Evidence(context.Context, *QueryEvidenceRequest) (*QueryEvidenceResponse, error) + // ListEvidences queries is a range query for evidences + ListEvidences(context.Context, *QueryListEvidencesRequest) (*QueryListEvidencesResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +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) ListPublicRandomness(ctx context.Context, req *QueryListPublicRandomnessRequest) (*QueryListPublicRandomnessResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPublicRandomness not implemented") +} +func (*UnimplementedQueryServer) Block(ctx context.Context, req *QueryBlockRequest) (*QueryBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Block not implemented") +} +func (*UnimplementedQueryServer) ListBlocks(ctx context.Context, req *QueryListBlocksRequest) (*QueryListBlocksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListBlocks not implemented") +} +func (*UnimplementedQueryServer) VotesAtHeight(ctx context.Context, req *QueryVotesAtHeightRequest) (*QueryVotesAtHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VotesAtHeight not implemented") +} +func (*UnimplementedQueryServer) Evidence(ctx context.Context, req *QueryEvidenceRequest) (*QueryEvidenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Evidence not implemented") +} +func (*UnimplementedQueryServer) ListEvidences(ctx context.Context, req *QueryListEvidencesRequest) (*QueryListEvidencesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListEvidences not implemented") +} + +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.finality.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_ListPublicRandomness_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListPublicRandomnessRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ListPublicRandomness(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/ListPublicRandomness", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ListPublicRandomness(ctx, req.(*QueryListPublicRandomnessRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Block_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Block(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/Block", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Block(ctx, req.(*QueryBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ListBlocks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListBlocksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ListBlocks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/ListBlocks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ListBlocks(ctx, req.(*QueryListBlocksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_VotesAtHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryVotesAtHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).VotesAtHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/VotesAtHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).VotesAtHeight(ctx, req.(*QueryVotesAtHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Evidence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryEvidenceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Evidence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/Evidence", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Evidence(ctx, req.(*QueryEvidenceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ListEvidences_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListEvidencesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ListEvidences(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Query/ListEvidences", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ListEvidences(ctx, req.(*QueryListEvidencesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "babylon.finality.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "ListPublicRandomness", + Handler: _Query_ListPublicRandomness_Handler, + }, + { + MethodName: "Block", + Handler: _Query_Block_Handler, + }, + { + MethodName: "ListBlocks", + Handler: _Query_ListBlocks_Handler, + }, + { + MethodName: "VotesAtHeight", + Handler: _Query_VotesAtHeight_Handler, + }, + { + MethodName: "Evidence", + Handler: _Query_Evidence_Handler, + }, + { + MethodName: "ListEvidences", + Handler: _Query_ListEvidences_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/finality/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 *QueryListPublicRandomnessRequest) 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 *QueryListPublicRandomnessRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListPublicRandomnessRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryListPublicRandomnessResponse) 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 *QueryListPublicRandomnessResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListPublicRandomnessResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.PubRandMap) > 0 { + for k := range m.PubRandMap { + v := m.PubRandMap[k] + baseI := i + if v != nil { + { + size := v.Size() + i -= size + if _, err := v.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i = encodeVarintQuery(dAtA, i, uint64(k)) + i-- + dAtA[i] = 0x8 + i = encodeVarintQuery(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryBlockRequest) 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 *QueryBlockRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBlockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryBlockResponse) 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 *QueryBlockResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Block != nil { + { + size, err := m.Block.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 *QueryListBlocksRequest) 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 *QueryListBlocksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListBlocksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Status != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryListBlocksResponse) 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 *QueryListBlocksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListBlocksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Blocks) > 0 { + for iNdEx := len(m.Blocks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Blocks[iNdEx].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 *QueryVotesAtHeightRequest) 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 *QueryVotesAtHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVotesAtHeightRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryVotesAtHeightResponse) 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 *QueryVotesAtHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVotesAtHeightResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BtcPks) > 0 { + for iNdEx := len(m.BtcPks) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.BtcPks[iNdEx].Size() + i -= size + if _, err := m.BtcPks[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryEvidenceRequest) 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 *QueryEvidenceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEvidenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FpBtcPkHex) > 0 { + i -= len(m.FpBtcPkHex) + copy(dAtA[i:], m.FpBtcPkHex) + i = encodeVarintQuery(dAtA, i, uint64(len(m.FpBtcPkHex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryEvidenceResponse) 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 *QueryEvidenceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEvidenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Evidence != nil { + { + size, err := m.Evidence.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 *QueryListEvidencesRequest) 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 *QueryListEvidencesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListEvidencesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.StartHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryListEvidencesResponse) 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 *QueryListEvidencesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListEvidencesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Evidences) > 0 { + for iNdEx := len(m.Evidences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Evidences[iNdEx].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 encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + 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 *QueryListPublicRandomnessRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryListPublicRandomnessResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PubRandMap) > 0 { + for k, v := range m.PubRandMap { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovQuery(uint64(l)) + } + mapEntrySize := 1 + sovQuery(uint64(k)) + l + n += mapEntrySize + 1 + sovQuery(uint64(mapEntrySize)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBlockRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryBlockResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Block != nil { + l = m.Block.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryListBlocksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Status != 0 { + n += 1 + sovQuery(uint64(m.Status)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryListBlocksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Blocks) > 0 { + for _, e := range m.Blocks { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryVotesAtHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryVotesAtHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BtcPks) > 0 { + for _, e := range m.BtcPks { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryEvidenceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FpBtcPkHex) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryEvidenceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Evidence != nil { + l = m.Evidence.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryListEvidencesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StartHeight != 0 { + n += 1 + sovQuery(uint64(m.StartHeight)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryListEvidencesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Evidences) > 0 { + for _, e := range m.Evidences { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +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 *QueryListPublicRandomnessRequest) 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: QueryListPublicRandomnessRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListPublicRandomnessRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryListPublicRandomnessResponse) 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: QueryListPublicRandomnessResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListPublicRandomnessResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubRandMap", 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 m.PubRandMap == nil { + m.PubRandMap = make(map[uint64]*github_com_babylonchain_babylon_types.SchnorrPubRand) + } + var mapkey uint64 + var mapvalue1 github_com_babylonchain_babylon_types.SchnorrPubRand + var mapvalue = &mapvalue1 + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return ErrInvalidLengthQuery + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex < 0 { + return ErrInvalidLengthQuery + } + if postbytesIndex > l { + return io.ErrUnexpectedEOF + } + if err := mapvalue.Unmarshal(dAtA[iNdEx:postbytesIndex]); err != nil { + return err + } + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.PubRandMap[mapkey] = ((*github_com_babylonchain_babylon_types.SchnorrPubRand)(mapvalue)) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryBlockRequest) 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: QueryBlockRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBlockRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryBlockResponse) 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: QueryBlockResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBlockResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Block", 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 m.Block == nil { + m.Block = &IndexedBlock{} + } + if err := m.Block.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 *QueryListBlocksRequest) 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: QueryListBlocksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListBlocksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= QueriedBlockStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryListBlocksResponse) 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: QueryListBlocksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListBlocksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Blocks", 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 + } + m.Blocks = append(m.Blocks, &IndexedBlock{}) + if err := m.Blocks[len(m.Blocks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 *QueryVotesAtHeightRequest) 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: QueryVotesAtHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVotesAtHeightRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryVotesAtHeightResponse) 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: QueryVotesAtHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVotesAtHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcPks", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.BtcPks = append(m.BtcPks, v) + if err := m.BtcPks[len(m.BtcPks)-1].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 *QueryEvidenceRequest) 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: QueryEvidenceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEvidenceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPkHex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FpBtcPkHex = string(dAtA[iNdEx:postIndex]) + 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 *QueryEvidenceResponse) 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: QueryEvidenceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEvidenceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", 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 m.Evidence == nil { + m.Evidence = &Evidence{} + } + if err := m.Evidence.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 *QueryListEvidencesRequest) 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: QueryListEvidencesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListEvidencesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.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 *QueryListEvidencesResponse) 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: QueryListEvidencesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListEvidencesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidences", 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 + } + m.Evidences = append(m.Evidences, &Evidence{}) + if err := m.Evidences[len(m.Evidences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", 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 m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.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 skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/finality/types/query.pb.gw.go b/x/finality/types/query.pb.gw.go new file mode 100644 index 000000000..1d001a7a0 --- /dev/null +++ b/x/finality/types/query.pb.gw.go @@ -0,0 +1,741 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: babylon/finality/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +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 + +} + +var ( + filter_Query_ListPublicRandomness_0 = &utilities.DoubleArray{Encoding: map[string]int{"fp_btc_pk_hex": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ListPublicRandomness_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPublicRandomnessRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPublicRandomness_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListPublicRandomness(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ListPublicRandomness_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPublicRandomnessRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPublicRandomness_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListPublicRandomness(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Block_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBlockRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.Block(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Block_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBlockRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.Block(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ListBlocks_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ListBlocks_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListBlocksRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListBlocks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListBlocks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ListBlocks_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListBlocksRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListBlocks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListBlocks(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_VotesAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVotesAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.VotesAtHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_VotesAtHeight_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVotesAtHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.VotesAtHeight(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Evidence_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEvidenceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := client.Evidence(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Evidence_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEvidenceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["fp_btc_pk_hex"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "fp_btc_pk_hex") + } + + protoReq.FpBtcPkHex, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "fp_btc_pk_hex", err) + } + + msg, err := server.Evidence(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ListEvidences_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ListEvidences_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListEvidencesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListEvidences_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListEvidences(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ListEvidences_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListEvidencesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListEvidences_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListEvidences(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// 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_ListPublicRandomness_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_ListPublicRandomness_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_ListPublicRandomness_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Block_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_Block_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_Block_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ListBlocks_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_ListBlocks_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_ListBlocks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_VotesAtHeight_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_VotesAtHeight_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_VotesAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Evidence_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_Evidence_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_Evidence_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ListEvidences_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_ListEvidences_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_ListEvidences_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "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_ListPublicRandomness_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_ListPublicRandomness_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_ListPublicRandomness_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Block_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_Block_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_Block_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ListBlocks_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_ListBlocks_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_ListBlocks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_VotesAtHeight_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_VotesAtHeight_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_VotesAtHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Evidence_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_Evidence_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_Evidence_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ListEvidences_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_ListEvidences_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_ListEvidences_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "finality", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ListPublicRandomness_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"babylon", "finality", "v1", "finality_providers", "fp_btc_pk_hex", "public_randomness_list"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Block_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylon", "finality", "v1", "blocks", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ListBlocks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "finality", "v1", "blocks"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_VotesAtHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylon", "finality", "v1", "votes", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Evidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"babylon", "finality", "v1", "finality_providers", "fp_btc_pk_hex", "evidence"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ListEvidences_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "finality", "v1", "evidences"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_ListPublicRandomness_0 = runtime.ForwardResponseMessage + + forward_Query_Block_0 = runtime.ForwardResponseMessage + + forward_Query_ListBlocks_0 = runtime.ForwardResponseMessage + + forward_Query_VotesAtHeight_0 = runtime.ForwardResponseMessage + + forward_Query_Evidence_0 = runtime.ForwardResponseMessage + + forward_Query_ListEvidences_0 = runtime.ForwardResponseMessage +) diff --git a/x/finality/types/tx.pb.go b/x/finality/types/tx.pb.go new file mode 100644 index 000000000..0579dbfd0 --- /dev/null +++ b/x/finality/types/tx.pb.go @@ -0,0 +1,1679 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/finality/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "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. +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 + +// MsgAddFinalitySig defines a message for adding a finality vote +type MsgAddFinalitySig struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // fp_btc_pk is the BTC PK of the finality provider that casts this vote + FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"` + // block_height is the height of the voted block + BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + // block_app_hash is the AppHash of the voted block + BlockAppHash []byte `protobuf:"bytes,4,opt,name=block_app_hash,json=blockAppHash,proto3" json:"block_app_hash,omitempty"` + // finality_sig is the finality signature to this block + // where finality signature is an EOTS signature, i.e., + // the `s` in a Schnorr signature `(r, s)` + // `r` is the public randomness that is already committed by the finality provider + FinalitySig *github_com_babylonchain_babylon_types.SchnorrEOTSSig `protobuf:"bytes,5,opt,name=finality_sig,json=finalitySig,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrEOTSSig" json:"finality_sig,omitempty"` +} + +func (m *MsgAddFinalitySig) Reset() { *m = MsgAddFinalitySig{} } +func (m *MsgAddFinalitySig) String() string { return proto.CompactTextString(m) } +func (*MsgAddFinalitySig) ProtoMessage() {} +func (*MsgAddFinalitySig) Descriptor() ([]byte, []int) { + return fileDescriptor_2dd6da066b6baf1d, []int{0} +} +func (m *MsgAddFinalitySig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddFinalitySig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddFinalitySig.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 *MsgAddFinalitySig) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddFinalitySig.Merge(m, src) +} +func (m *MsgAddFinalitySig) XXX_Size() int { + return m.Size() +} +func (m *MsgAddFinalitySig) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddFinalitySig.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddFinalitySig proto.InternalMessageInfo + +func (m *MsgAddFinalitySig) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgAddFinalitySig) GetBlockHeight() uint64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *MsgAddFinalitySig) GetBlockAppHash() []byte { + if m != nil { + return m.BlockAppHash + } + return nil +} + +// MsgAddFinalitySigResponse is the response to the MsgAddFinalitySig message +type MsgAddFinalitySigResponse struct { +} + +func (m *MsgAddFinalitySigResponse) Reset() { *m = MsgAddFinalitySigResponse{} } +func (m *MsgAddFinalitySigResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAddFinalitySigResponse) ProtoMessage() {} +func (*MsgAddFinalitySigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2dd6da066b6baf1d, []int{1} +} +func (m *MsgAddFinalitySigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddFinalitySigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddFinalitySigResponse.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 *MsgAddFinalitySigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddFinalitySigResponse.Merge(m, src) +} +func (m *MsgAddFinalitySigResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAddFinalitySigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddFinalitySigResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddFinalitySigResponse proto.InternalMessageInfo + +// MsgCommitPubRandList defines a message for committing a list of public randomness for EOTS +type MsgCommitPubRandList struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // fp_btc_pk is the BTC PK of the finality provider that commits the public randomness + FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"` + // start_height is the start block height of the list of public randomness + StartHeight uint64 `protobuf:"varint,3,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + // pub_rand_list is the list of public randomness + PubRandList []github_com_babylonchain_babylon_types.SchnorrPubRand `protobuf:"bytes,4,rep,name=pub_rand_list,json=pubRandList,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrPubRand" json:"pub_rand_list,omitempty"` + // sig is the signature on (start_height || pub_rand_list) signed by + // SK corresponding to fp_btc_pk. This prevents others to commit public + // randomness on behalf of fp_btc_pk + // TODO: another option is to restrict signer to correspond to fp_btc_pk. This restricts + // the tx submitter to be the holder of fp_btc_pk. Decide this later + Sig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,5,opt,name=sig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"sig,omitempty"` +} + +func (m *MsgCommitPubRandList) Reset() { *m = MsgCommitPubRandList{} } +func (m *MsgCommitPubRandList) String() string { return proto.CompactTextString(m) } +func (*MsgCommitPubRandList) ProtoMessage() {} +func (*MsgCommitPubRandList) Descriptor() ([]byte, []int) { + return fileDescriptor_2dd6da066b6baf1d, []int{2} +} +func (m *MsgCommitPubRandList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCommitPubRandList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCommitPubRandList.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 *MsgCommitPubRandList) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCommitPubRandList.Merge(m, src) +} +func (m *MsgCommitPubRandList) XXX_Size() int { + return m.Size() +} +func (m *MsgCommitPubRandList) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCommitPubRandList.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCommitPubRandList proto.InternalMessageInfo + +func (m *MsgCommitPubRandList) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgCommitPubRandList) GetStartHeight() uint64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +// MsgCommitPubRandListResponse is the response to the MsgCommitPubRandList message +type MsgCommitPubRandListResponse struct { +} + +func (m *MsgCommitPubRandListResponse) Reset() { *m = MsgCommitPubRandListResponse{} } +func (m *MsgCommitPubRandListResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCommitPubRandListResponse) ProtoMessage() {} +func (*MsgCommitPubRandListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2dd6da066b6baf1d, []int{3} +} +func (m *MsgCommitPubRandListResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCommitPubRandListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCommitPubRandListResponse.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 *MsgCommitPubRandListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCommitPubRandListResponse.Merge(m, src) +} +func (m *MsgCommitPubRandListResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCommitPubRandListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCommitPubRandListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCommitPubRandListResponse proto.InternalMessageInfo + +// MsgUpdateParams defines a message for updating finality 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 finality 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_2dd6da066b6baf1d, []int{4} +} +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_2dd6da066b6baf1d, []int{5} +} +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((*MsgAddFinalitySig)(nil), "babylon.finality.v1.MsgAddFinalitySig") + proto.RegisterType((*MsgAddFinalitySigResponse)(nil), "babylon.finality.v1.MsgAddFinalitySigResponse") + proto.RegisterType((*MsgCommitPubRandList)(nil), "babylon.finality.v1.MsgCommitPubRandList") + proto.RegisterType((*MsgCommitPubRandListResponse)(nil), "babylon.finality.v1.MsgCommitPubRandListResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "babylon.finality.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.finality.v1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("babylon/finality/v1/tx.proto", fileDescriptor_2dd6da066b6baf1d) } + +var fileDescriptor_2dd6da066b6baf1d = []byte{ + // 644 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0x8d, 0x93, 0xb4, 0x9f, 0x3a, 0xc9, 0x57, 0x54, 0x53, 0x51, 0x37, 0xad, 0x9c, 0x10, 0x55, + 0xa8, 0x54, 0x60, 0xf7, 0x8f, 0x8a, 0x76, 0xd7, 0x20, 0x50, 0xa1, 0x44, 0x44, 0x0e, 0x6c, 0x00, + 0xc9, 0x1a, 0xff, 0x64, 0x3c, 0x6a, 0xec, 0x19, 0x3c, 0x93, 0xaa, 0xd9, 0x21, 0x9e, 0x80, 0x05, + 0x0f, 0x52, 0x21, 0x1e, 0xa2, 0x1b, 0xa4, 0x8a, 0x15, 0xaa, 0x44, 0x84, 0xda, 0x45, 0x5f, 0x03, + 0xc5, 0x1e, 0x37, 0x6d, 0x92, 0x8a, 0xc0, 0x82, 0x9d, 0xef, 0x9c, 0x33, 0xf7, 0xdc, 0x7b, 0xe6, + 0x5e, 0x83, 0x79, 0x0b, 0x5a, 0xed, 0x26, 0x09, 0xf4, 0x06, 0x0e, 0x60, 0x13, 0xf3, 0xb6, 0xbe, + 0xbf, 0xa2, 0xf3, 0x03, 0x8d, 0x86, 0x84, 0x13, 0xf9, 0xa6, 0x40, 0xb5, 0x04, 0xd5, 0xf6, 0x57, + 0x0a, 0xd3, 0x88, 0x20, 0x12, 0xe1, 0x7a, 0xf7, 0x2b, 0xa6, 0x16, 0x66, 0x6d, 0xc2, 0x7c, 0xc2, + 0xcc, 0x18, 0x88, 0x03, 0x01, 0xcd, 0xc4, 0x91, 0xee, 0x33, 0xd4, 0xcd, 0xee, 0x33, 0x24, 0x80, + 0xd2, 0x30, 0x71, 0x0a, 0x43, 0xe8, 0x8b, 0xab, 0xe5, 0xcf, 0x69, 0x30, 0x55, 0x65, 0x68, 0xdb, + 0x71, 0x9e, 0x08, 0x4a, 0x1d, 0x23, 0xf9, 0x16, 0x18, 0x67, 0x18, 0x05, 0x6e, 0xa8, 0x48, 0x25, + 0x69, 0x71, 0xc2, 0x10, 0x91, 0x6c, 0x80, 0x89, 0x06, 0x35, 0x2d, 0x6e, 0x9b, 0x74, 0x4f, 0x49, + 0x97, 0xa4, 0xc5, 0x7c, 0x65, 0xe3, 0xa4, 0x53, 0x5c, 0x45, 0x98, 0x7b, 0x2d, 0x4b, 0xb3, 0x89, + 0xaf, 0x0b, 0x45, 0xdb, 0x83, 0x38, 0x48, 0x02, 0x9d, 0xb7, 0xa9, 0xcb, 0xb4, 0xca, 0xd3, 0xda, + 0xda, 0xfa, 0x72, 0xad, 0x65, 0xed, 0xba, 0x6d, 0xe3, 0xbf, 0x06, 0xad, 0x70, 0xbb, 0xb6, 0x27, + 0xdf, 0x06, 0x79, 0xab, 0x49, 0xec, 0x3d, 0xd3, 0x73, 0x31, 0xf2, 0xb8, 0x92, 0x29, 0x49, 0x8b, + 0x59, 0x23, 0x17, 0x9d, 0xed, 0x44, 0x47, 0xf2, 0x02, 0x98, 0x8c, 0x29, 0x90, 0x52, 0xd3, 0x83, + 0xcc, 0x53, 0xb2, 0x5d, 0x6d, 0x23, 0xbe, 0xb8, 0x4d, 0xe9, 0x0e, 0x64, 0x9e, 0xfc, 0x06, 0xe4, + 0x93, 0x36, 0x4d, 0x86, 0x91, 0x32, 0x16, 0xd5, 0xf7, 0xf0, 0xa4, 0x53, 0x5c, 0x1f, 0xad, 0xbe, + 0xba, 0xed, 0x05, 0x24, 0x0c, 0x1f, 0xbf, 0x78, 0x59, 0xaf, 0x63, 0x64, 0xe4, 0x1a, 0x3d, 0x47, + 0xb6, 0x72, 0x1f, 0xce, 0x0f, 0x97, 0x84, 0x0d, 0xe5, 0x39, 0x30, 0x3b, 0xe0, 0x99, 0xe1, 0x32, + 0x4a, 0x02, 0xe6, 0x96, 0x7f, 0xa4, 0xc1, 0x74, 0x95, 0xa1, 0x47, 0xc4, 0xf7, 0x31, 0xaf, 0xb5, + 0x2c, 0x03, 0x06, 0xce, 0x73, 0xcc, 0xf8, 0xbf, 0x36, 0x95, 0x71, 0x18, 0xf2, 0x3e, 0x53, 0xa3, + 0x33, 0x61, 0xea, 0x5b, 0xf0, 0x3f, 0x6d, 0x59, 0x66, 0x08, 0x03, 0xc7, 0x6c, 0x62, 0xc6, 0x95, + 0x6c, 0x29, 0xf3, 0x57, 0x7e, 0x89, 0x1e, 0x8d, 0x1c, 0xbd, 0xd4, 0xec, 0x2e, 0xc8, 0xf4, 0xde, + 0x60, 0xf3, 0xa4, 0x53, 0x7c, 0xf0, 0x27, 0xed, 0xd4, 0x31, 0x0a, 0x20, 0x6f, 0x85, 0xae, 0xd1, + 0xcd, 0x72, 0xd5, 0x7c, 0x15, 0xcc, 0x0f, 0xb3, 0xf7, 0xc2, 0xff, 0x4f, 0x12, 0xb8, 0x51, 0x65, + 0xe8, 0x15, 0x75, 0x20, 0x77, 0x6b, 0xd1, 0xac, 0xcb, 0x1b, 0x60, 0x02, 0xb6, 0xb8, 0x47, 0x42, + 0xcc, 0xdb, 0xb1, 0xfb, 0x15, 0xe5, 0xdb, 0x97, 0xfb, 0xd3, 0x62, 0x8b, 0xb6, 0x1d, 0x27, 0x74, + 0x19, 0xab, 0xf3, 0x10, 0x07, 0xc8, 0xe8, 0x51, 0xe5, 0x4d, 0x30, 0x1e, 0x6f, 0x4b, 0xf4, 0x2e, + 0xb9, 0xd5, 0x39, 0x6d, 0xc8, 0xbe, 0x6a, 0xb1, 0x48, 0x25, 0x7b, 0xd4, 0x29, 0xa6, 0x0c, 0x71, + 0x61, 0x6b, 0xb2, 0x5b, 0x73, 0x2f, 0x55, 0x79, 0x16, 0xcc, 0xf4, 0x55, 0x95, 0x54, 0xbc, 0xfa, + 0x35, 0x0d, 0x32, 0x55, 0x86, 0x64, 0x0f, 0x4c, 0xf6, 0xed, 0xe1, 0x9d, 0xa1, 0x7a, 0x03, 0xb3, + 0x57, 0xd0, 0x46, 0xe3, 0x25, 0x8a, 0xf2, 0x3b, 0x30, 0x35, 0x38, 0x9f, 0x77, 0xaf, 0x4b, 0x32, + 0x40, 0x2d, 0xac, 0x8c, 0x4c, 0xbd, 0x90, 0xb4, 0x40, 0xfe, 0xca, 0x93, 0x2c, 0x5c, 0x97, 0xe2, + 0x32, 0xab, 0x70, 0x6f, 0x14, 0x56, 0xa2, 0x51, 0x18, 0x7b, 0x7f, 0x7e, 0xb8, 0x24, 0x55, 0x9e, + 0x1d, 0x9d, 0xaa, 0xd2, 0xf1, 0xa9, 0x2a, 0xfd, 0x3c, 0x55, 0xa5, 0x8f, 0x67, 0x6a, 0xea, 0xf8, + 0x4c, 0x4d, 0x7d, 0x3f, 0x53, 0x53, 0xaf, 0x97, 0x7f, 0x37, 0x84, 0x07, 0xbd, 0x3f, 0x65, 0x34, + 0x8f, 0xd6, 0x78, 0xf4, 0x9b, 0x5c, 0xfb, 0x15, 0x00, 0x00, 0xff, 0xff, 0x01, 0xd6, 0x89, 0xb4, + 0xc7, 0x05, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// 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 { + // AddFinalitySig adds a finality signature to a given block + AddFinalitySig(ctx context.Context, in *MsgAddFinalitySig, opts ...grpc.CallOption) (*MsgAddFinalitySigResponse, error) + // CommitPubRandList commits a list of public randomness for EOTS + CommitPubRandList(ctx context.Context, in *MsgCommitPubRandList, opts ...grpc.CallOption) (*MsgCommitPubRandListResponse, error) + // TODO: msg for evidence of equivocation. this is not specified yet + // UpdateParams updates the finality module parameters. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) AddFinalitySig(ctx context.Context, in *MsgAddFinalitySig, opts ...grpc.CallOption) (*MsgAddFinalitySigResponse, error) { + out := new(MsgAddFinalitySigResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Msg/AddFinalitySig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CommitPubRandList(ctx context.Context, in *MsgCommitPubRandList, opts ...grpc.CallOption) (*MsgCommitPubRandListResponse, error) { + out := new(MsgCommitPubRandListResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.v1.Msg/CommitPubRandList", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.finality.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 { + // AddFinalitySig adds a finality signature to a given block + AddFinalitySig(context.Context, *MsgAddFinalitySig) (*MsgAddFinalitySigResponse, error) + // CommitPubRandList commits a list of public randomness for EOTS + CommitPubRandList(context.Context, *MsgCommitPubRandList) (*MsgCommitPubRandListResponse, error) + // TODO: msg for evidence of equivocation. this is not specified yet + // UpdateParams updates the finality module parameters. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) AddFinalitySig(ctx context.Context, req *MsgAddFinalitySig) (*MsgAddFinalitySigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddFinalitySig not implemented") +} +func (*UnimplementedMsgServer) CommitPubRandList(ctx context.Context, req *MsgCommitPubRandList) (*MsgCommitPubRandListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CommitPubRandList not implemented") +} +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_AddFinalitySig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAddFinalitySig) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AddFinalitySig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Msg/AddFinalitySig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AddFinalitySig(ctx, req.(*MsgAddFinalitySig)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CommitPubRandList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCommitPubRandList) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CommitPubRandList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.finality.v1.Msg/CommitPubRandList", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CommitPubRandList(ctx, req.(*MsgCommitPubRandList)) + } + return interceptor(ctx, in, info, handler) +} + +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.finality.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.finality.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddFinalitySig", + Handler: _Msg_AddFinalitySig_Handler, + }, + { + MethodName: "CommitPubRandList", + Handler: _Msg_CommitPubRandList_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/finality/v1/tx.proto", +} + +func (m *MsgAddFinalitySig) 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 *MsgAddFinalitySig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddFinalitySig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FinalitySig != nil { + { + size := m.FinalitySig.Size() + i -= size + if _, err := m.FinalitySig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.BlockAppHash) > 0 { + i -= len(m.BlockAppHash) + copy(dAtA[i:], m.BlockAppHash) + i = encodeVarintTx(dAtA, i, uint64(len(m.BlockAppHash))) + i-- + dAtA[i] = 0x22 + } + if m.BlockHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x18 + } + if m.FpBtcPk != nil { + { + size := m.FpBtcPk.Size() + i -= size + if _, err := m.FpBtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgAddFinalitySigResponse) 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 *MsgAddFinalitySigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddFinalitySigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCommitPubRandList) 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 *MsgCommitPubRandList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCommitPubRandList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sig != nil { + { + size := m.Sig.Size() + i -= size + if _, err := m.Sig.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.PubRandList) > 0 { + for iNdEx := len(m.PubRandList) - 1; iNdEx >= 0; iNdEx-- { + { + size := m.PubRandList[iNdEx].Size() + i -= size + if _, err := m.PubRandList[iNdEx].MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.StartHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x18 + } + if m.FpBtcPk != nil { + { + size := m.FpBtcPk.Size() + i -= size + if _, err := m.FpBtcPk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCommitPubRandListResponse) 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 *MsgCommitPubRandListResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCommitPubRandListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +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 *MsgAddFinalitySig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.FpBtcPk != nil { + l = m.FpBtcPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.BlockHeight != 0 { + n += 1 + sovTx(uint64(m.BlockHeight)) + } + l = len(m.BlockAppHash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.FinalitySig != nil { + l = m.FinalitySig.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgAddFinalitySigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCommitPubRandList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.FpBtcPk != nil { + l = m.FpBtcPk.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.StartHeight != 0 { + n += 1 + sovTx(uint64(m.StartHeight)) + } + if len(m.PubRandList) > 0 { + for _, e := range m.PubRandList { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.Sig != nil { + l = m.Sig.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCommitPubRandListResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +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 *MsgAddFinalitySig) 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: MsgAddFinalitySig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddFinalitySig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPk = &v + if err := m.FpBtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockAppHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockAppHash = append(m.BlockAppHash[:0], dAtA[iNdEx:postIndex]...) + if m.BlockAppHash == nil { + m.BlockAppHash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalitySig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.SchnorrEOTSSig + m.FinalitySig = &v + if err := m.FinalitySig.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 *MsgAddFinalitySigResponse) 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: MsgAddFinalitySigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddFinalitySigResponse: 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 (m *MsgCommitPubRandList) 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: MsgCommitPubRandList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCommitPubRandList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FpBtcPk", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340PubKey + m.FpBtcPk = &v + if err := m.FpBtcPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubRandList", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.SchnorrPubRand + m.PubRandList = append(m.PubRandList, v) + if err := m.PubRandList[len(m.PubRandList)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BIP340Signature + m.Sig = &v + if err := m.Sig.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 *MsgCommitPubRandListResponse) 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: MsgCommitPubRandListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCommitPubRandListResponse: 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 (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") +) diff --git a/x/finality/types/types.go b/x/finality/types/types.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/x/finality/types/types.go @@ -0,0 +1 @@ +package types diff --git a/x/incentive/abci.go b/x/incentive/abci.go new file mode 100644 index 000000000..66cdb7da9 --- /dev/null +++ b/x/incentive/abci.go @@ -0,0 +1,31 @@ +package incentive + +import ( + "context" + "time" + + "github.com/babylonchain/babylon/x/incentive/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + // handle coins in the fee collector account, including + // - send a portion of coins in the fee collector account to the incentive module account + // - accumulate BTC staking gauge at the current height + // - accumulate BTC timestamping gauge at the current epoch + if sdk.UnwrapSDKContext(ctx).HeaderInfo().Height > 0 { + k.HandleCoinsInFeeCollector(ctx) + } + return nil +} + +func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + + return []abci.ValidatorUpdate{}, nil +} diff --git a/x/incentive/client/cli/query.go b/x/incentive/client/cli/query.go new file mode 100644 index 000000000..de89a08d9 --- /dev/null +++ b/x/incentive/client/cli/query.go @@ -0,0 +1,132 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string) *cobra.Command { + // Group incentive queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + CmdQueryParams(), + CmdQueryRewardGauges(), + CmdQueryBTCStakingGauge(), + CmdQueryBTCTimestampingGauge(), + ) + + return cmd +} + +func CmdQueryRewardGauges() *cobra.Command { + cmd := &cobra.Command{ + Use: "reward-gauges [address]", + Short: "shows reward gauges of a given stakeholder address", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryRewardGaugesRequest{ + Address: args[0], + } + res, err := queryClient.RewardGauges(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdQueryBTCStakingGauge() *cobra.Command { + cmd := &cobra.Command{ + Use: "btc-staking-gauge [height]", + Short: "shows BTC staking gauge of a given height", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + height, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryBTCStakingGaugeRequest{ + Height: height, + } + res, err := queryClient.BTCStakingGauge(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func CmdQueryBTCTimestampingGauge() *cobra.Command { + cmd := &cobra.Command{ + Use: "btc-timestamping-gauge [epoch]", + Short: "shows BTC timestamping gauge of a given epoch", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + epoch, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryBTCTimestampingGaugeRequest{ + EpochNum: epoch, + } + res, err := queryClient.BTCTimestampingGauge(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/incentive/client/cli/query_params.go b/x/incentive/client/cli/query_params.go new file mode 100644 index 000000000..f6c20a168 --- /dev/null +++ b/x/incentive/client/cli/query_params.go @@ -0,0 +1,36 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/x/incentive/types" +) + +func CmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "shows the parameters of the module", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/incentive/client/cli/tx.go b/x/incentive/client/cli/tx.go new file mode 100644 index 000000000..d039820c2 --- /dev/null +++ b/x/incentive/client/cli/tx.go @@ -0,0 +1,53 @@ +package cli + +import ( + "fmt" + "github.com/spf13/cobra" + + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + NewWithdrawRewardCmd(), + ) + + return cmd +} + +func NewWithdrawRewardCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-reward [type]", + Short: "withdraw reward of the stakeholder behind the transaction submitter in a given type (one of {submitter, reporter, finality_provider, btc_delegation})", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.MsgWithdrawReward{ + Type: args[0], + Address: clientCtx.FromAddress.String(), + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/incentive/genesis.go b/x/incentive/genesis.go new file mode 100644 index 000000000..1122dbdba --- /dev/null +++ b/x/incentive/genesis.go @@ -0,0 +1,22 @@ +package incentive + +import ( + "context" + "github.com/babylonchain/babylon/x/incentive/keeper" + "github.com/babylonchain/babylon/x/incentive/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns the module's exported genesis +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Params = k.GetParams(ctx) + + return genesis +} diff --git a/x/incentive/genesis_test.go b/x/incentive/genesis_test.go new file mode 100644 index 000000000..bb3f7e509 --- /dev/null +++ b/x/incentive/genesis_test.go @@ -0,0 +1,25 @@ +package incentive_test + +import ( + "testing" + + keepertest "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/testutil/nullify" + "github.com/babylonchain/babylon/x/incentive" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/stretchr/testify/require" +) + +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + } + + k, ctx := keepertest.IncentiveKeeper(t, nil, nil, nil) + incentive.InitGenesis(ctx, *k, genesisState) + got := incentive.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + nullify.Fill(&genesisState) + nullify.Fill(got) +} diff --git a/x/incentive/keeper/btc_staking_gauge.go b/x/incentive/keeper/btc_staking_gauge.go new file mode 100644 index 000000000..b3067ab65 --- /dev/null +++ b/x/incentive/keeper/btc_staking_gauge.go @@ -0,0 +1,82 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + bstypes "github.com/babylonchain/babylon/x/btcstaking/types" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RewardBTCStaking distributes rewards to finality providers/delegations at a given height according +// to the reward distribution cache +// (adapted from https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/x/distribution/keeper/allocation.go#L12-L64) +func (k Keeper) RewardBTCStaking(ctx context.Context, height uint64, rdc *bstypes.RewardDistCache) { + gauge := k.GetBTCStakingGauge(ctx, height) + if gauge == nil { + // failing to get a reward gauge at previous height is a programming error + panic("failed to get a reward gauge at previous height") + } + // reward each of the finality provider and its BTC delegations in proportion + for _, fp := range rdc.FinalityProviders { + // get coins that will be allocated to the finality provider and its BTC delegations + fpPortion := rdc.GetFinalityProviderPortion(fp) + coinsForFpsAndDels := gauge.GetCoinsPortion(fpPortion) + // reward the finality provider with commission + coinsForCommission := types.GetCoinsPortion(coinsForFpsAndDels, *fp.Commission) + k.accumulateRewardGauge(ctx, types.FinalityProviderType, fp.GetAddress(), coinsForCommission) + // reward the rest of coins to each BTC delegation proportional to its voting power portion + coinsForBTCDels := coinsForFpsAndDels.Sub(coinsForCommission...) + for _, btcDel := range fp.BtcDels { + btcDelPortion := fp.GetBTCDelPortion(btcDel) + coinsForDel := types.GetCoinsPortion(coinsForBTCDels, btcDelPortion) + k.accumulateRewardGauge(ctx, types.BTCDelegationType, btcDel.GetAddress(), coinsForDel) + } + } + + // TODO: handle the change in the gauge due to the truncating operations +} + +func (k Keeper) accumulateBTCStakingReward(ctx context.Context, btcStakingReward sdk.Coins) { + // update BTC staking gauge + height := uint64(sdk.UnwrapSDKContext(ctx).HeaderInfo().Height) + gauge := types.NewGauge(btcStakingReward...) + k.SetBTCStakingGauge(ctx, height, gauge) + + // transfer the BTC staking reward from fee collector account to incentive module account + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, btcStakingReward) + if err != nil { + // this can only be programming error and is unrecoverable + panic(err) + } +} + +func (k Keeper) SetBTCStakingGauge(ctx context.Context, height uint64, gauge *types.Gauge) { + store := k.btcStakingGaugeStore(ctx) + gaugeBytes := k.cdc.MustMarshal(gauge) + store.Set(sdk.Uint64ToBigEndian(height), gaugeBytes) +} + +func (k Keeper) GetBTCStakingGauge(ctx context.Context, height uint64) *types.Gauge { + store := k.btcStakingGaugeStore(ctx) + gaugeBytes := store.Get(sdk.Uint64ToBigEndian(height)) + if gaugeBytes == nil { + return nil + } + + var gauge types.Gauge + k.cdc.MustUnmarshal(gaugeBytes, &gauge) + return &gauge +} + +// btcStakingGaugeStore returns the KVStore of the gauge of total reward for +// BTC staking at each height +// prefix: BTCStakingGaugeKey +// key: gauge height +// value: gauge of rewards for BTC staking at this height +func (k Keeper) btcStakingGaugeStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.BTCStakingGaugeKey) +} diff --git a/x/incentive/keeper/btc_staking_gauge_test.go b/x/incentive/keeper/btc_staking_gauge_test.go new file mode 100644 index 000000000..e467829c1 --- /dev/null +++ b/x/incentive/keeper/btc_staking_gauge_test.go @@ -0,0 +1,85 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzRewardBTCStaking(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock bank keeper + bankKeeper := types.NewMockBankKeeper(ctrl) + + // create incentive keeper + keeper, ctx := testkeeper.IncentiveKeeper(t, bankKeeper, nil, nil) + height := datagen.RandomInt(r, 1000) + ctx = datagen.WithCtxHeight(ctx, height) + + // set a random gauge + gauge := datagen.GenRandomGauge(r) + keeper.SetBTCStakingGauge(ctx, height, gauge) + + // generate a random reward distribution cache + rdc, err := datagen.GenRandomBTCStakingRewardDistCache(r) + require.NoError(t, err) + + // expected values + distributedCoins := sdk.NewCoins() + fpRewardMap := map[string]sdk.Coins{} // key: address, value: reward + btcDelRewardMap := map[string]sdk.Coins{} // key: address, value: reward + + for _, fp := range rdc.FinalityProviders { + fpPortion := rdc.GetFinalityProviderPortion(fp) + coinsForFpsAndDels := gauge.GetCoinsPortion(fpPortion) + coinsForCommission := types.GetCoinsPortion(coinsForFpsAndDels, *fp.Commission) + if coinsForCommission.IsAllPositive() { + fpRewardMap[fp.GetAddress().String()] = coinsForCommission + distributedCoins.Add(coinsForCommission...) + } + coinsForBTCDels := coinsForFpsAndDels.Sub(coinsForCommission...) + for _, btcDel := range fp.BtcDels { + btcDelPortion := fp.GetBTCDelPortion(btcDel) + coinsForDel := types.GetCoinsPortion(coinsForBTCDels, btcDelPortion) + if coinsForDel.IsAllPositive() { + btcDelRewardMap[btcDel.GetAddress().String()] = coinsForDel + distributedCoins.Add(coinsForDel...) + } + } + } + + // distribute rewards in the gauge to finality providers/delegations + keeper.RewardBTCStaking(ctx, height, rdc) + + // assert consistency between reward map and reward gauge + for addrStr, reward := range fpRewardMap { + addr, err := sdk.AccAddressFromBech32(addrStr) + require.NoError(t, err) + rg := keeper.GetRewardGauge(ctx, types.FinalityProviderType, addr) + require.NotNil(t, rg) + require.Equal(t, reward, rg.Coins) + } + for addrStr, reward := range btcDelRewardMap { + addr, err := sdk.AccAddressFromBech32(addrStr) + require.NoError(t, err) + rg := keeper.GetRewardGauge(ctx, types.BTCDelegationType, addr) + require.NotNil(t, rg) + require.Equal(t, reward, rg.Coins) + } + + // assert distributedCoins is a subset of coins in gauge + require.True(t, gauge.Coins.IsAllGTE(distributedCoins)) + }) +} diff --git a/x/incentive/keeper/btc_timestamping_gauge.go b/x/incentive/keeper/btc_timestamping_gauge.go new file mode 100644 index 000000000..96e5b28a7 --- /dev/null +++ b/x/incentive/keeper/btc_timestamping_gauge.go @@ -0,0 +1,121 @@ +package keeper + +import ( + "context" + "cosmossdk.io/math" + "cosmossdk.io/store/prefix" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RewardBTCTimestamping distributes rewards to submitters/reporters of a checkpoint at a given epoch +// according to the reward distribution cache +func (k Keeper) RewardBTCTimestamping(ctx context.Context, epoch uint64, rdi *btcctypes.RewardDistInfo) { + gauge := k.GetBTCTimestampingGauge(ctx, epoch) + if gauge == nil { + // failing to get a reward gauge at a finalised epoch is a programming error + panic("failed to get a reward gauge at a finalized epoch") + } + + params := k.GetParams(ctx) + btcTimestampingPortion := params.BTCTimestampingPortion() + // TODO: parameterise bestPortion + bestPortion := math.LegacyNewDecWithPrec(80, 2) // 80 * 10^{-2} = 0.8 + + // distribute coins to best submitter + submitterPortion := params.SubmitterPortion.QuoTruncate(btcTimestampingPortion) + coinsToSubmitters := gauge.GetCoinsPortion(submitterPortion) + coinsToBestSubmitter := types.GetCoinsPortion(coinsToSubmitters, bestPortion) + k.accumulateRewardGauge(ctx, types.SubmitterType, rdi.Best.Submitter, coinsToBestSubmitter) + restCoinsToSubmitters := coinsToSubmitters.Sub(coinsToBestSubmitter...) + + // distribute coins to best reporter + reporterPortion := params.ReporterPortion.QuoTruncate(btcTimestampingPortion) + coinsToReporters := gauge.GetCoinsPortion(reporterPortion) + coinsToBestReporter := types.GetCoinsPortion(coinsToReporters, bestPortion) + k.accumulateRewardGauge(ctx, types.ReporterType, rdi.Best.Reporter, coinsToBestReporter) + restCoinsToReporters := coinsToReporters.Sub(coinsToBestReporter...) + + // if there is only 1 submission, distribute the rest to submitter and reporter, then skip the rest logic + if len(rdi.Others) == 0 { + // give rest coins to the best submitter + k.accumulateRewardGauge(ctx, types.SubmitterType, rdi.Best.Submitter, restCoinsToSubmitters) + // give rest coins to the best reporter + k.accumulateRewardGauge(ctx, types.ReporterType, rdi.Best.Reporter, restCoinsToReporters) + // skip the rest logic + return + } + + // distribute the rest to each of the other submitters + // TODO: our tokenomics might specify weights for the rest submitters in the future + eachOtherSubmitterPortion := math.LegacyOneDec().QuoTruncate(math.LegacyOneDec().MulInt64(int64(len(rdi.Others)))) + coinsToEachOtherSubmitter := types.GetCoinsPortion(restCoinsToSubmitters, eachOtherSubmitterPortion) + if coinsToEachOtherSubmitter.IsAllPositive() { + for _, submission := range rdi.Others { + k.accumulateRewardGauge(ctx, types.SubmitterType, submission.Submitter, coinsToEachOtherSubmitter) + } + } + + // distribute the rest to each of the other reporters + // TODO: our tokenomics might specify weights for the rest reporters in the future + eachOtherReporterPortion := math.LegacyOneDec().QuoTruncate(math.LegacyOneDec().MulInt64(int64(len(rdi.Others)))) + coinsToEachOtherReporter := types.GetCoinsPortion(restCoinsToReporters, eachOtherReporterPortion) + if coinsToEachOtherReporter.IsAllPositive() { + for _, submission := range rdi.Others { + k.accumulateRewardGauge(ctx, types.ReporterType, submission.Reporter, coinsToEachOtherReporter) + } + } +} + +func (k Keeper) accumulateBTCTimestampingReward(ctx context.Context, btcTimestampingReward sdk.Coins) { + epoch := k.epochingKeeper.GetEpoch(ctx) + + // update BTC timestamping reward gauge + gauge := k.GetBTCTimestampingGauge(ctx, epoch.EpochNumber) + if gauge == nil { + // if this epoch does not have a gauge yet, create a new one + gauge = types.NewGauge(btcTimestampingReward...) + } else { + // if this epoch already has a gauge, accumulate coins in the gauge + gauge.Coins = gauge.Coins.Add(btcTimestampingReward...) + } + + k.SetBTCTimestampingGauge(ctx, epoch.EpochNumber, gauge) + + // transfer the BTC timestamping reward from fee collector account to incentive module account + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, btcTimestampingReward) + if err != nil { + // this can only be programming error and is unrecoverable + panic(err) + } +} + +func (k Keeper) SetBTCTimestampingGauge(ctx context.Context, epoch uint64, gauge *types.Gauge) { + store := k.btcTimestampingGaugeStore(ctx) + gaugeBytes := k.cdc.MustMarshal(gauge) + store.Set(sdk.Uint64ToBigEndian(epoch), gaugeBytes) +} + +func (k Keeper) GetBTCTimestampingGauge(ctx context.Context, epoch uint64) *types.Gauge { + store := k.btcTimestampingGaugeStore(ctx) + gaugeBytes := store.Get(sdk.Uint64ToBigEndian(epoch)) + if gaugeBytes == nil { + return nil + } + + var gauge types.Gauge + k.cdc.MustUnmarshal(gaugeBytes, &gauge) + return &gauge +} + +// btcTimestampingGaugeStore returns the KVStore of the gauge of total reward for +// BTC timestamping at each epoch +// prefix: BTCTimestampingGaugeKey +// key: epoch number +// value: gauge of rewards for BTC timestamping at this epoch +func (k Keeper) btcTimestampingGaugeStore(ctx context.Context) prefix.Store { + storeAdaptor := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdaptor, types.BTCTimestampingGaugeKey) +} diff --git a/x/incentive/keeper/btc_timestamping_gauge_test.go b/x/incentive/keeper/btc_timestamping_gauge_test.go new file mode 100644 index 000000000..3b42cae19 --- /dev/null +++ b/x/incentive/keeper/btc_timestamping_gauge_test.go @@ -0,0 +1,123 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + "cosmossdk.io/math" + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func FuzzRewardBTCTimestamping(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock bank keeper + bankKeeper := types.NewMockBankKeeper(ctrl) + + // create incentive keeper + keeper, ctx := testkeeper.IncentiveKeeper(t, bankKeeper, nil, nil) + epoch := datagen.RandomInt(r, 1000) + 1 + + // set a random gauge + gauge := datagen.GenRandomGauge(r) + keeper.SetBTCTimestampingGauge(ctx, epoch, gauge) + + // generate a random BTC timestamping reward distribution info + rdi := datagen.GenRandomBTCTimestampingRewardDistInfo(r) + + // parameters + params := types.DefaultParams() + btcTimestampingPortion := params.BTCTimestampingPortion() + bestPortion := math.LegacyNewDecWithPrec(80, 2) // 80 * 10^{-2} = 0.8 + + // expected values + distributedCoins := sdk.NewCoins() + submitterRewardMap := map[string]sdk.Coins{} // key: address, value: reward + reporterRewardMap := map[string]sdk.Coins{} // key: address, value: reward + + submitterPortion := params.SubmitterPortion.QuoTruncate(btcTimestampingPortion) + coinsToSubmitters := gauge.GetCoinsPortion(submitterPortion) + coinsToBestSubmitter := types.GetCoinsPortion(coinsToSubmitters, bestPortion) + if coinsToBestSubmitter.IsAllPositive() { + submitterRewardMap[rdi.Best.Submitter.String()] = coinsToBestSubmitter + distributedCoins.Add(coinsToBestSubmitter...) + } + // best reporter + reporterPortion := params.ReporterPortion.QuoTruncate(btcTimestampingPortion) + coinsToReporters := gauge.GetCoinsPortion(reporterPortion) + coinsToBestReporter := types.GetCoinsPortion(coinsToReporters, bestPortion) + if coinsToBestReporter.IsAllPositive() { + reporterRewardMap[rdi.Best.Reporter.String()] = coinsToBestReporter + distributedCoins.Add(coinsToBestReporter...) + } + // other submitters and reporters + if len(rdi.Others) > 0 { + // other submitters + coinsToOtherSubmitters := coinsToSubmitters.Sub(coinsToBestSubmitter...) + eachOtherSubmitterPortion := math.LegacyOneDec().QuoTruncate(math.LegacyOneDec().MulInt64(int64(len(rdi.Others)))) + coinsToEachOtherSubmitter := types.GetCoinsPortion(coinsToOtherSubmitters, eachOtherSubmitterPortion) + if coinsToEachOtherSubmitter.IsAllPositive() { + for _, submission := range rdi.Others { + submitterRewardMap[submission.Submitter.String()] = coinsToEachOtherSubmitter + distributedCoins.Add(coinsToEachOtherSubmitter...) + } + } + // other reporters + coinsToOtherReporters := coinsToReporters.Sub(coinsToBestReporter...) + eachOtherReporterPortion := math.LegacyOneDec().QuoTruncate(math.LegacyOneDec().MulInt64(int64(len(rdi.Others)))) + coinsToEachOtherReporter := types.GetCoinsPortion(coinsToOtherReporters, eachOtherReporterPortion) + if coinsToEachOtherReporter.IsAllPositive() { + for _, submission := range rdi.Others { + reporterRewardMap[submission.Reporter.String()] = coinsToEachOtherReporter + distributedCoins.Add(coinsToEachOtherReporter...) + } + } + } else { + // no other submission. give rest coins to best submitter/reporter + // give rest coins to the best submitter + restCoinsToSubmitter := coinsToSubmitters.Sub(coinsToBestSubmitter...) + if restCoinsToSubmitter.IsAllPositive() { + submitterRewardMap[rdi.Best.Submitter.String()] = submitterRewardMap[rdi.Best.Submitter.String()].Add(restCoinsToSubmitter...) + distributedCoins.Add(restCoinsToSubmitter...) + } + // give rest coins to the best reporter + restCoinsToReporter := coinsToReporters.Sub(coinsToBestReporter...) + if restCoinsToReporter.IsAllPositive() { + reporterRewardMap[rdi.Best.Reporter.String()] = reporterRewardMap[rdi.Best.Reporter.String()].Add(restCoinsToSubmitter...) + distributedCoins.Add(restCoinsToReporter...) + } + } + + // distribute rewards in the gauge to reporters/submitters + keeper.RewardBTCTimestamping(ctx, epoch, rdi) + + // assert consistency between reward map and reward gauge + for addrStr, reward := range submitterRewardMap { + addr, err := sdk.AccAddressFromBech32(addrStr) + require.NoError(t, err) + rg := keeper.GetRewardGauge(ctx, types.SubmitterType, addr) + require.NotNil(t, rg) + require.Equal(t, reward, rg.Coins) + } + for addrStr, reward := range reporterRewardMap { + addr, err := sdk.AccAddressFromBech32(addrStr) + require.NoError(t, err) + rg := keeper.GetRewardGauge(ctx, types.ReporterType, addr) + require.NotNil(t, rg) + require.Equal(t, reward, rg.Coins) + } + + // assert distributedCoins is a subset of coins in gauge + require.True(t, gauge.Coins.IsAllGTE(distributedCoins)) + }) +} diff --git a/x/incentive/keeper/grpc_query.go b/x/incentive/keeper/grpc_query.go new file mode 100644 index 000000000..341359b7d --- /dev/null +++ b/x/incentive/keeper/grpc_query.go @@ -0,0 +1,73 @@ +package keeper + +import ( + "context" + + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ types.QueryServer = Keeper{} + +func (k Keeper) RewardGauges(goCtx context.Context, req *types.QueryRewardGaugesRequest) (*types.QueryRewardGaugesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + // try to cast address + address, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + rgMap := map[string]*types.RewardGauge{} + + // find reward gauge + for _, sType := range types.GetAllStakeholderTypes() { + rg := k.GetRewardGauge(ctx, sType, address) + if rg == nil { + continue + } + rgMap[sType.String()] = rg + } + + // return error if no reward gauge is found + if len(rgMap) == 0 { + return nil, types.ErrRewardGaugeNotFound + } + + return &types.QueryRewardGaugesResponse{RewardGauges: rgMap}, nil +} + +func (k Keeper) BTCStakingGauge(goCtx context.Context, req *types.QueryBTCStakingGaugeRequest) (*types.QueryBTCStakingGaugeResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + // find gauge + gauge := k.GetBTCStakingGauge(ctx, req.Height) + if gauge == nil { + return nil, types.ErrBTCStakingGaugeNotFound + } + + return &types.QueryBTCStakingGaugeResponse{Gauge: gauge}, nil +} + +func (k Keeper) BTCTimestampingGauge(goCtx context.Context, req *types.QueryBTCTimestampingGaugeRequest) (*types.QueryBTCTimestampingGaugeResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + // find gauge + gauge := k.GetBTCTimestampingGauge(ctx, req.EpochNum) + if gauge == nil { + return nil, types.ErrBTCTimestampingGaugeNotFound + } + + return &types.QueryBTCTimestampingGaugeResponse{Gauge: gauge}, nil +} diff --git a/x/incentive/keeper/grpc_query_test.go b/x/incentive/keeper/grpc_query_test.go new file mode 100644 index 000000000..9d609f307 --- /dev/null +++ b/x/incentive/keeper/grpc_query_test.go @@ -0,0 +1,125 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func FuzzRewardGaugesQuery(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + keeper, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + + // generate a list of random RewardGauge map and insert them to KVStore + // where in each map, key is stakeholder type and address is the reward gauge + rgMaps := []map[string]*types.RewardGauge{} + sAddrList := []sdk.AccAddress{} + numRgMaps := datagen.RandomInt(r, 100) + for i := uint64(0); i < numRgMaps; i++ { + rgMap := map[string]*types.RewardGauge{} + sAddr := datagen.GenRandomAccount().GetAddress() + sAddrList = append(sAddrList, sAddr) + for i := uint64(0); i <= datagen.RandomInt(r, 4); i++ { + sType := datagen.GenRandomStakeholderType(r) + rg := datagen.GenRandomRewardGauge(r) + rgMap[sType.String()] = rg + + keeper.SetRewardGauge(ctx, sType, sAddr, rg) + } + rgMaps = append(rgMaps, rgMap) + } + + // query existence and assert consistency + for i := range rgMaps { + req := &types.QueryRewardGaugesRequest{ + Address: sAddrList[i].String(), + } + resp, err := keeper.RewardGauges(ctx, req) + require.NoError(t, err) + for sTypeStr, rg := range rgMaps[i] { + require.Equal(t, rg.Coins, resp.RewardGauges[sTypeStr].Coins) + } + } + }) +} + +func FuzzBTCStakingGaugeQuery(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + keeper, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + + // generate a list of random Gauges at random heights, then insert them to KVStore + heightList := []uint64{datagen.RandomInt(r, 1000) + 1} + gaugeList := []*types.Gauge{datagen.GenRandomGauge(r)} + keeper.SetBTCStakingGauge(ctx, heightList[0], gaugeList[0]) + + numGauges := datagen.RandomInt(r, 100) + 1 + for i := uint64(1); i < numGauges; i++ { + height := heightList[i-1] + datagen.RandomInt(r, 100) + 1 + heightList = append(heightList, height) + gauge := datagen.GenRandomGauge(r) + gaugeList = append(gaugeList, gauge) + keeper.SetBTCStakingGauge(ctx, height, gauge) + } + + // query existence and assert consistency + for i := range gaugeList { + req := &types.QueryBTCStakingGaugeRequest{ + Height: heightList[i], + } + resp, err := keeper.BTCStakingGauge(ctx, req) + require.NoError(t, err) + require.True(t, resp.Gauge.Coins.Equal(gaugeList[i].Coins)) + } + }) +} + +func FuzzBTCTimestampingGaugeQuery(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + keeper, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + + // initialise the 1st gauge + epochList := []uint64{datagen.RandomInt(r, 1000) + 1} + gaugeList := []*types.Gauge{datagen.GenRandomGauge(r)} + keeper.SetBTCTimestampingGauge(ctx, epochList[0], gaugeList[0]) + + // generate a list of random gauges at random heights, then insert them to KVStore + numGauges := datagen.RandomInt(r, 100) + 1 + for i := uint64(1); i < numGauges; i++ { + // increment a random number of epochs + epoch := epochList[i-1] + datagen.RandomInt(r, 1000) + 1 + epochList = append(epochList, epoch) + gauge := datagen.GenRandomGauge(r) + gaugeList = append(gaugeList, gauge) + keeper.SetBTCTimestampingGauge(ctx, epoch, gauge) + } + + // query existence and assert consistency + for i := range gaugeList { + req := &types.QueryBTCTimestampingGaugeRequest{ + EpochNum: epochList[i], + } + resp, err := keeper.BTCTimestampingGauge(ctx, req) + require.NoError(t, err) + require.True(t, resp.Gauge.Coins.Equal(gaugeList[i].Coins), + "epoch: %d\nresp.Gauge.Coins: %s\ngaugeList[i].Coins: %s\n", + epochList[i], + resp.Gauge.Coins.Sort().String(), + gaugeList[i].Coins.Sort().String(), + ) + } + }) +} diff --git a/x/incentive/keeper/intercept_fee_collector.go b/x/incentive/keeper/intercept_fee_collector.go new file mode 100644 index 000000000..88500e558 --- /dev/null +++ b/x/incentive/keeper/intercept_fee_collector.go @@ -0,0 +1,40 @@ +package keeper + +import ( + "context" + "github.com/babylonchain/babylon/x/incentive/types" +) + +// HandleCoinsInFeeCollector intercepts a portion of coins in fee collector, and distributes +// them to BTC staking gauge and BTC timestamping gauge of the current height and epoch, respectively. +// It is invoked upon every `BeginBlock`. +// adapted from https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/x/distribution/keeper/allocation.go#L15-L26 +func (k Keeper) HandleCoinsInFeeCollector(ctx context.Context) { + params := k.GetParams(ctx) + + // find the fee collector account + feeCollector := k.accountKeeper.GetModuleAccount(ctx, k.feeCollectorName) + // get all balances in the fee collector account, + // where the balance includes minted tokens in the previous block + feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) + + // don't intercept if there is no fee in fee collector account + if !feesCollectedInt.IsAllPositive() { + return + } + + // record BTC staking gauge for the current height, and transfer corresponding amount + // from fee collector account to incentive module account + // TODO: maybe we should not transfer reward to BTC staking gauge before BTC staking is activated + // this is tricky to implement since finality module will depend on incentive and incentive cannot + // depend on finality module due to cyclic dependency + btcStakingPortion := params.BTCStakingPortion() + btcStakingReward := types.GetCoinsPortion(feesCollectedInt, btcStakingPortion) + k.accumulateBTCStakingReward(ctx, btcStakingReward) + + // record BTC timestamping gauge for the current epoch, and transfer corresponding amount + // from fee collector account to incentive module account + btcTimestampingPortion := params.BTCTimestampingPortion() + btcTimestampingReward := types.GetCoinsPortion(feesCollectedInt, btcTimestampingPortion) + k.accumulateBTCTimestampingReward(ctx, btcTimestampingReward) +} diff --git a/x/incentive/keeper/intercept_fee_collector_test.go b/x/incentive/keeper/intercept_fee_collector_test.go new file mode 100644 index 000000000..189be6ba1 --- /dev/null +++ b/x/incentive/keeper/intercept_fee_collector_test.go @@ -0,0 +1,91 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +var ( + feeCollectorAcc = authtypes.NewEmptyModuleAccount(authtypes.FeeCollectorName) + fees = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))) +) + +func FuzzInterceptFeeCollector(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock bank keeper + bankKeeper := types.NewMockBankKeeper(ctrl) + bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees).Times(1) + + // mock account keeper + accountKeeper := types.NewMockAccountKeeper(ctrl) + accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), authtypes.FeeCollectorName).Return(feeCollectorAcc).Times(1) + + // mock epoching keeper + epochNum := datagen.RandomInt(r, 100) + 1 + epochingKeeper := types.NewMockEpochingKeeper(ctrl) + epochingKeeper.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: epochNum}).Times(1) + + keeper, ctx := testkeeper.IncentiveKeeper(t, bankKeeper, accountKeeper, epochingKeeper) + height := datagen.RandomInt(r, 1000) + ctx = datagen.WithCtxHeight(ctx, height) + + // mock (thus ensure) that fees with the exact portion is intercepted + // NOTE: if the actual fees are different from feesForIncentive the test will fail + params := keeper.GetParams(ctx) + feesForBTCStaking := types.GetCoinsPortion(fees, params.BTCStakingPortion()) + feesForBTCTimestamping := types.GetCoinsPortion(fees, params.BTCTimestampingPortion()) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), gomock.Eq(authtypes.FeeCollectorName), gomock.Eq(types.ModuleName), gomock.Eq(feesForBTCStaking)).Times(1) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), gomock.Eq(authtypes.FeeCollectorName), gomock.Eq(types.ModuleName), gomock.Eq(feesForBTCTimestamping)).Times(1) + + // handle coins in fee collector + keeper.HandleCoinsInFeeCollector(ctx) + + // assert correctness of BTC staking gauge at height + btcStakingFee := types.GetCoinsPortion(fees, params.BTCStakingPortion()) + btcStakingGauge := keeper.GetBTCStakingGauge(ctx, height) + require.NotNil(t, btcStakingGauge) + require.Equal(t, btcStakingFee, btcStakingGauge.Coins) + + // assert correctness of BTC timestamping gauge at epoch + btcTimestampingFee := types.GetCoinsPortion(fees, params.BTCTimestampingPortion()) + btcTimestampingGauge := keeper.GetBTCTimestampingGauge(ctx, epochNum) + require.NotNil(t, btcTimestampingGauge) + require.Equal(t, btcTimestampingFee, btcTimestampingGauge.Coins) + + // accumulate for this epoch again and see if the epoch's BTC timestamping gauge has accumulated or not + height += 1 + ctx = datagen.WithCtxHeight(ctx, height) + bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees).Times(1) + accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), authtypes.FeeCollectorName).Return(feeCollectorAcc).Times(1) + epochingKeeper.EXPECT().GetEpoch(gomock.Any()).Return(&epochingtypes.Epoch{EpochNumber: epochNum}).Times(1) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), gomock.Eq(authtypes.FeeCollectorName), gomock.Eq(types.ModuleName), gomock.Eq(feesForBTCStaking)).Times(1) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), gomock.Eq(authtypes.FeeCollectorName), gomock.Eq(types.ModuleName), gomock.Eq(feesForBTCTimestamping)).Times(1) + // handle coins in fee collector + keeper.HandleCoinsInFeeCollector(ctx) + // assert BTC timestamping gauge has doubled + btcTimestampingGauge2 := keeper.GetBTCTimestampingGauge(ctx, epochNum) + require.NotNil(t, btcTimestampingGauge2) + for i := range btcTimestampingGauge.Coins { + amount := btcTimestampingGauge.Coins[i].Amount.Uint64() + amount2 := btcTimestampingGauge2.Coins[i].Amount.Uint64() + require.Equal(t, amount*2, amount2) + } + }) +} diff --git a/x/incentive/keeper/keeper.go b/x/incentive/keeper/keeper.go new file mode 100644 index 000000000..4d931f4fe --- /dev/null +++ b/x/incentive/keeper/keeper.go @@ -0,0 +1,51 @@ +package keeper + +import ( + corestoretypes "cosmossdk.io/core/store" + "fmt" + + "cosmossdk.io/log" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ( + Keeper struct { + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService + + epochingKeeper types.EpochingKeeper + bankKeeper types.BankKeeper + accountKeeper types.AccountKeeper + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string + // name of the FeeCollector ModuleAccount + feeCollectorName string + } +) + +func NewKeeper( + cdc codec.BinaryCodec, + storeService corestoretypes.KVStoreService, + bankKeeper types.BankKeeper, + accountKeeper types.AccountKeeper, + epochingKeeper types.EpochingKeeper, + authority string, + feeCollectorName string, +) Keeper { + return Keeper{ + cdc: cdc, + storeService: storeService, + epochingKeeper: epochingKeeper, + bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + authority: authority, + feeCollectorName: feeCollectorName, + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} diff --git a/x/incentive/keeper/msg_server.go b/x/incentive/keeper/msg_server.go new file mode 100644 index 000000000..3d9ff5399 --- /dev/null +++ b/x/incentive/keeper/msg_server.go @@ -0,0 +1,67 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// UpdateParams updates the params +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) + } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := ms.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} + +// WithdrawReward withdraws the reward of a given stakeholder +func (ms msgServer) WithdrawReward(goCtx context.Context, req *types.MsgWithdrawReward) (*types.MsgWithdrawRewardResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // get stakeholder type and address + sType, err := types.NewStakeHolderTypeFromString(req.Type) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + addr, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + // withdraw reward, i.e., send withdrawable reward to the stakeholder address and clear the reward gauge + withdrawnCoins, err := ms.withdrawReward(ctx, sType, addr) + if err != nil { + return nil, err + } + + // all good + return &types.MsgWithdrawRewardResponse{ + Coins: withdrawnCoins, + }, nil +} diff --git a/x/incentive/keeper/msg_server_test.go b/x/incentive/keeper/msg_server_test.go new file mode 100644 index 000000000..5b68ded8d --- /dev/null +++ b/x/incentive/keeper/msg_server_test.go @@ -0,0 +1,65 @@ +package keeper_test + +import ( + "context" + "math/rand" + "testing" + + "github.com/babylonchain/babylon/testutil/datagen" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func setupMsgServer(t testing.TB) (types.MsgServer, context.Context) { + k, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + return keeper.NewMsgServerImpl(*k), ctx +} + +func TestMsgServer(t *testing.T) { + ms, ctx := setupMsgServer(t) + require.NotNil(t, ms) + require.NotNil(t, ctx) +} + +func FuzzWithdrawReward(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // mock bank keeper + bk := types.NewMockBankKeeper(ctrl) + + ik, ctx := testkeeper.IncentiveKeeper(t, bk, nil, nil) + ms := keeper.NewMsgServerImpl(*ik) + + // generate and set a random reward gauge with a random set of withdrawable coins + rg := datagen.GenRandomRewardGauge(r) + rg.WithdrawnCoins = datagen.GenRandomWithdrawnCoins(r, rg.Coins) + sType := datagen.GenRandomStakeholderType(r) + sAddr := datagen.GenRandomAccount().GetAddress() + ik.SetRewardGauge(ctx, sType, sAddr, rg) + + // mock transfer of withdrawable coins + withdrawableCoins := rg.GetWithdrawableCoins() + bk.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), gomock.Eq(types.ModuleName), gomock.Eq(sAddr), gomock.Eq(withdrawableCoins)).Times(1) + + // invoke withdraw and assert consistency + resp, err := ms.WithdrawReward(ctx, &types.MsgWithdrawReward{ + Type: sType.String(), + Address: sAddr.String(), + }) + require.NoError(t, err) + require.Equal(t, withdrawableCoins, resp.Coins) + + // ensure reward gauge is now empty + newRg := ik.GetRewardGauge(ctx, sType, sAddr) + require.NotNil(t, newRg) + require.True(t, newRg.IsFullyWithdrawn()) + }) +} diff --git a/x/incentive/keeper/params.go b/x/incentive/keeper/params.go new file mode 100644 index 000000000..4e8c80e72 --- /dev/null +++ b/x/incentive/keeper/params.go @@ -0,0 +1,30 @@ +package keeper + +import ( + "context" + "github.com/babylonchain/babylon/x/incentive/types" +) + +// SetParams sets the x/incentive module parameters. +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { + if err := p.Validate(); err != nil { + return err + } + store := k.storeService.OpenKVStore(ctx) + bz := k.cdc.MustMarshal(&p) + return store.Set(types.ParamsKey, bz) +} + +// GetParams returns the current x/incentive module parameters. +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } + if bz == nil { + return p + } + k.cdc.MustUnmarshal(bz, &p) + return p +} diff --git a/x/incentive/keeper/params_test.go b/x/incentive/keeper/params_test.go new file mode 100644 index 000000000..e2ee0a1da --- /dev/null +++ b/x/incentive/keeper/params_test.go @@ -0,0 +1,19 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/stretchr/testify/require" +) + +func TestGetParams(t *testing.T) { + k, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + params := types.DefaultParams() + + err := k.SetParams(ctx, params) + require.NoError(t, err) + + require.EqualValues(t, params, k.GetParams(ctx)) +} diff --git a/x/incentive/keeper/query_params.go b/x/incentive/keeper/query_params.go new file mode 100644 index 000000000..b58c8c1ec --- /dev/null +++ b/x/incentive/keeper/query_params.go @@ -0,0 +1,19 @@ +package keeper + +import ( + "context" + + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil +} diff --git a/x/incentive/keeper/query_params_test.go b/x/incentive/keeper/query_params_test.go new file mode 100644 index 000000000..c98188c76 --- /dev/null +++ b/x/incentive/keeper/query_params_test.go @@ -0,0 +1,20 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/stretchr/testify/require" +) + +func TestParamsQuery(t *testing.T) { + keeper, ctx := testkeeper.IncentiveKeeper(t, nil, nil, nil) + params := types.DefaultParams() + err := keeper.SetParams(ctx, params) + require.NoError(t, err) + + response, err := keeper.Params(ctx, &types.QueryParamsRequest{}) + require.NoError(t, err) + require.Equal(t, &types.QueryParamsResponse{Params: params}, response) +} diff --git a/x/incentive/keeper/reward_gauge.go b/x/incentive/keeper/reward_gauge.go new file mode 100644 index 000000000..d06cbf3fd --- /dev/null +++ b/x/incentive/keeper/reward_gauge.go @@ -0,0 +1,77 @@ +package keeper + +import ( + "context" + "cosmossdk.io/store/prefix" + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) withdrawReward(ctx context.Context, sType types.StakeholderType, addr sdk.AccAddress) (sdk.Coins, error) { + // retrieve reward gauge of the given stakeholder + rg := k.GetRewardGauge(ctx, sType, addr) + if rg == nil { + return nil, types.ErrRewardGaugeNotFound + } + // get withdrawable coins + withdrawableCoins := rg.GetWithdrawableCoins() + if !withdrawableCoins.IsAllPositive() { + return nil, types.ErrNoWithdrawableCoins + } + // transfer withdrawable coins from incentive module account to the stakeholder's address + if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, withdrawableCoins); err != nil { + return nil, err + } + // empty reward gauge + rg.SetFullyWithdrawn() + k.SetRewardGauge(ctx, sType, addr, rg) + // all good, return + return withdrawableCoins, nil +} + +// accumulateRewardGauge accumulates the given reward of of a given stakeholder in a given type +func (k Keeper) accumulateRewardGauge(ctx context.Context, sType types.StakeholderType, addr sdk.AccAddress, reward sdk.Coins) { + // if reward contains nothing, do nothing + if !reward.IsAllPositive() { + return + } + // get reward gauge, or create a new one if it does not exist + rg := k.GetRewardGauge(ctx, sType, addr) + if rg == nil { + rg = types.NewRewardGauge() + } + // add the given reward to reward gauge + rg.Add(reward) + // set back + k.SetRewardGauge(ctx, sType, addr, rg) +} + +func (k Keeper) SetRewardGauge(ctx context.Context, sType types.StakeholderType, addr sdk.AccAddress, rg *types.RewardGauge) { + store := k.rewardGaugeStore(ctx, sType) + rgBytes := k.cdc.MustMarshal(rg) + store.Set(addr.Bytes(), rgBytes) +} + +func (k Keeper) GetRewardGauge(ctx context.Context, sType types.StakeholderType, addr sdk.AccAddress) *types.RewardGauge { + store := k.rewardGaugeStore(ctx, sType) + rgBytes := store.Get(addr.Bytes()) + if rgBytes == nil { + return nil + } + + var rg types.RewardGauge + k.cdc.MustUnmarshal(rgBytes, &rg) + return &rg +} + +// rewardGaugeStore returns the KVStore of the reward gauge of a stakeholder +// of a given type {submitter, reporter, finality provider, BTC delegation} +// prefix: RewardGaugeKey +// key: (stakeholder type || stakeholder address) +// value: reward gauge +func (k Keeper) rewardGaugeStore(ctx context.Context, sType types.StakeholderType) prefix.Store { + storeAdaptor := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + rgStore := prefix.NewStore(storeAdaptor, types.RewardGaugeKey) + return prefix.NewStore(rgStore, sType.Bytes()) +} diff --git a/x/incentive/keeper/reward_gauge_test.go b/x/incentive/keeper/reward_gauge_test.go new file mode 100644 index 000000000..74a840c13 --- /dev/null +++ b/x/incentive/keeper/reward_gauge_test.go @@ -0,0 +1,20 @@ +package keeper_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/incentive/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestEmptyRewardGauge(t *testing.T) { + emptyRewardGauge := &types.RewardGauge{ + Coins: sdk.NewCoins(), + WithdrawnCoins: sdk.NewCoins(), + } + rgBytes, err := emptyRewardGauge.Marshal() + require.NoError(t, err) + require.NotNil(t, rgBytes) // the marshaled empty reward gauge is not nil + require.True(t, len(rgBytes) == 0) // the marshalled empty reward gauge has 0 bytes +} diff --git a/x/incentive/module.go b/x/incentive/module.go new file mode 100644 index 000000000..862daf4ff --- /dev/null +++ b/x/incentive/module.go @@ -0,0 +1,154 @@ +package incentive + +import ( + "context" + "cosmossdk.io/core/appmodule" + "encoding/json" + "fmt" + + "github.com/babylonchain/babylon/x/incentive/client/cli" + "github.com/babylonchain/babylon/x/incentive/keeper" + "github.com/babylonchain/babylon/x/incentive/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" +) + +var ( + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, + accountKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted) +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) +} + +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { + return EndBlocker(ctx, am.keeper) +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker +} diff --git a/x/incentive/types/codec.go b/x/incentive/types/codec.go new file mode 100644 index 000000000..6abb8fc7f --- /dev/null +++ b/x/incentive/types/codec.go @@ -0,0 +1,29 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgWithdrawReward{}, "incentive/MsgWithdrawReward", nil) + cdc.RegisterConcrete(&MsgUpdateParams{}, "incentive/MsgUpdateParams", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + // Register messages + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgWithdrawReward{}, + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/incentive/types/errors.go b/x/incentive/types/errors.go new file mode 100644 index 000000000..3e56f8d36 --- /dev/null +++ b/x/incentive/types/errors.go @@ -0,0 +1,13 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +// x/incentive module sentinel errors +var ( + ErrBTCStakingGaugeNotFound = errorsmod.Register(ModuleName, 1100, "BTC staking gauge not found") + ErrBTCTimestampingGaugeNotFound = errorsmod.Register(ModuleName, 1101, "BTC timestamping gauge not found") + ErrRewardGaugeNotFound = errorsmod.Register(ModuleName, 1102, "reward gauge not found") + ErrNoWithdrawableCoins = errorsmod.Register(ModuleName, 1103, "no coin is withdrawable") +) diff --git a/x/incentive/types/expected_keepers.go b/x/incentive/types/expected_keepers.go new file mode 100644 index 000000000..a8000ec2d --- /dev/null +++ b/x/incentive/types/expected_keepers.go @@ -0,0 +1,23 @@ +package types + +import ( + "context" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type AccountKeeper interface { + GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI + GetModuleAccount(ctx context.Context, name string) sdk.ModuleAccountI +} + +type BankKeeper interface { + SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins + GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins + SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error +} + +type EpochingKeeper interface { + GetEpoch(ctx context.Context) *epochingtypes.Epoch +} diff --git a/x/incentive/types/genesis.go b/x/incentive/types/genesis.go new file mode 100644 index 000000000..9d633ecd7 --- /dev/null +++ b/x/incentive/types/genesis.go @@ -0,0 +1,14 @@ +package types + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + return gs.Params.Validate() +} diff --git a/x/incentive/types/genesis.pb.go b/x/incentive/types/genesis.pb.go new file mode 100644 index 000000000..d2c7b135c --- /dev/null +++ b/x/incentive/types/genesis.pb.go @@ -0,0 +1,321 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/incentive/genesis.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 + +// GenesisState defines the incentive module's genesis state. +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_41d5400dc6b4b931, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "babylon.incentive.GenesisState") +} + +func init() { proto.RegisterFile("babylon/incentive/genesis.proto", fileDescriptor_41d5400dc6b4b931) } + +var fileDescriptor_41d5400dc6b4b931 = []byte{ + // 195 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0xcf, 0xcc, 0x4b, 0x4e, 0xcd, 0x2b, 0xc9, 0x2c, 0x4b, 0xd5, 0x4f, 0x4f, + 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0x2a, 0xd0, + 0x83, 0x2b, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83, 0x58, 0x10, 0x85, 0x52, + 0x72, 0x98, 0x26, 0x15, 0x24, 0x16, 0x25, 0xe6, 0x42, 0x0d, 0x52, 0x72, 0xe7, 0xe2, 0x71, 0x87, + 0x98, 0x1c, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x64, 0xce, 0xc5, 0x06, 0x91, 0x97, 0x60, 0x54, 0x60, + 0xd4, 0xe0, 0x36, 0x92, 0xd4, 0xc3, 0xb0, 0x49, 0x2f, 0x00, 0xac, 0xc0, 0x89, 0xe5, 0xc4, 0x3d, + 0x79, 0x86, 0x20, 0xa8, 0x72, 0x27, 0xef, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, + 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, + 0x88, 0x32, 0x4c, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0x1a, 0x96, + 0x9c, 0x91, 0x98, 0x99, 0x07, 0xe3, 0xe8, 0x57, 0x20, 0x39, 0xae, 0xa4, 0xb2, 0x20, 0xb5, 0x38, + 0x89, 0x0d, 0xec, 0x38, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0xa1, 0x97, 0x7e, 0x08, + 0x01, 0x00, 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) 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 = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: 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 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:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/incentive/types/genesis_test.go b/x/incentive/types/genesis_test.go new file mode 100644 index 000000000..230e2c670 --- /dev/null +++ b/x/incentive/types/genesis_test.go @@ -0,0 +1,32 @@ +package types_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/incentive/types" + "github.com/stretchr/testify/require" +) + +func TestGenesisState_Validate(t *testing.T) { + tests := []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/incentive/types/incentive.go b/x/incentive/types/incentive.go new file mode 100644 index 000000000..9b0184cb8 --- /dev/null +++ b/x/incentive/types/incentive.go @@ -0,0 +1,118 @@ +package types + +import ( + "fmt" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewGauge(coins ...sdk.Coin) *Gauge { + return &Gauge{ + Coins: coins, + } +} + +func (g *Gauge) GetCoinsPortion(portion math.LegacyDec) sdk.Coins { + return GetCoinsPortion(g.Coins, portion) +} + +func NewRewardGauge(coins ...sdk.Coin) *RewardGauge { + return &RewardGauge{ + Coins: coins, + WithdrawnCoins: sdk.NewCoins(), + } +} + +// GetWithdrawableCoins returns withdrawable coins in this reward gauge +func (rg *RewardGauge) GetWithdrawableCoins() sdk.Coins { + return rg.Coins.Sub(rg.WithdrawnCoins...) +} + +// SetFullyWithdrawn makes the reward gauge to have no withdrawable coins +// typically called after the stakeholder withdraws its reward +func (rg *RewardGauge) SetFullyWithdrawn() { + rg.WithdrawnCoins = sdk.NewCoins(rg.Coins...) +} + +// IsFullyWithdrawn returns whether the reward gauge has nothing to withdraw +func (rg *RewardGauge) IsFullyWithdrawn() bool { + return rg.Coins.Equal(rg.WithdrawnCoins) +} + +func (rg *RewardGauge) Add(coins sdk.Coins) { + rg.Coins = rg.Coins.Add(coins...) +} + +func GetCoinsPortion(coinsInt sdk.Coins, portion math.LegacyDec) sdk.Coins { + // coins with decimal value + coins := sdk.NewDecCoinsFromCoins(coinsInt...) + // portion of coins with decimal values + portionCoins := coins.MulDecTruncate(portion) + // truncate back + // TODO: how to deal with changes? + portionCoinsInt, _ := portionCoins.TruncateDecimal() + return portionCoinsInt +} + +// StakeholderType enum for stakeholder type, used as key prefix in KVStore +type StakeholderType byte + +const ( + SubmitterType StakeholderType = iota + ReporterType + FinalityProviderType + BTCDelegationType +) + +func GetAllStakeholderTypes() []StakeholderType { + return []StakeholderType{SubmitterType, ReporterType, FinalityProviderType, BTCDelegationType} +} + +func NewStakeHolderType(stBytes []byte) (StakeholderType, error) { + if len(stBytes) != 1 { + return SubmitterType, fmt.Errorf("invalid format for stBytes") + } + if stBytes[0] == byte(SubmitterType) { + return SubmitterType, nil + } else if stBytes[0] == byte(ReporterType) { + return ReporterType, nil + } else if stBytes[0] == byte(FinalityProviderType) { + return FinalityProviderType, nil + } else if stBytes[0] == byte(BTCDelegationType) { + return BTCDelegationType, nil + } else { + return SubmitterType, fmt.Errorf("invalid stBytes") + } +} + +func NewStakeHolderTypeFromString(stStr string) (StakeholderType, error) { + if stStr == "submitter" { + return SubmitterType, nil + } else if stStr == "reporter" { + return ReporterType, nil + } else if stStr == "finality_provider" { + return FinalityProviderType, nil + } else if stStr == "btc_delegation" { + return BTCDelegationType, nil + } else { + return SubmitterType, fmt.Errorf("invalid stStr") + } +} + +func (st StakeholderType) Bytes() []byte { + return []byte{byte(st)} +} + +func (st StakeholderType) String() string { + if st == SubmitterType { + return "submitter" + } else if st == ReporterType { + return "reporter" + } else if st == FinalityProviderType { + return "finality_provider" + } else if st == BTCDelegationType { + return "btc_delegation" + } + panic("invalid stakeholder type") +} diff --git a/x/incentive/types/incentive.pb.go b/x/incentive/types/incentive.pb.go new file mode 100644 index 000000000..304f85dba --- /dev/null +++ b/x/incentive/types/incentive.pb.go @@ -0,0 +1,587 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/incentive/incentive.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "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 + +// Gauge is an object that stores rewards to be distributed +// code adapted from https://github.com/osmosis-labs/osmosis/blob/v18.0.0/proto/osmosis/incentives/gauge.proto +type Gauge struct { + // coins are coins that have been in the gauge + // Can have multiple coin denoms + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` +} + +func (m *Gauge) Reset() { *m = Gauge{} } +func (m *Gauge) String() string { return proto.CompactTextString(m) } +func (*Gauge) ProtoMessage() {} +func (*Gauge) Descriptor() ([]byte, []int) { + return fileDescriptor_3954bc4942045a7a, []int{0} +} +func (m *Gauge) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Gauge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Gauge.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 *Gauge) XXX_Merge(src proto.Message) { + xxx_messageInfo_Gauge.Merge(m, src) +} +func (m *Gauge) XXX_Size() int { + return m.Size() +} +func (m *Gauge) XXX_DiscardUnknown() { + xxx_messageInfo_Gauge.DiscardUnknown(m) +} + +var xxx_messageInfo_Gauge proto.InternalMessageInfo + +func (m *Gauge) GetCoins() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Coins + } + return nil +} + +// RewardGauge is an object that stores rewards distributed to a BTC staking/timestamping stakeholder +// code adapted from https://github.com/osmosis-labs/osmosis/blob/v18.0.0/proto/osmosis/incentives/gauge.proto +type RewardGauge struct { + // coins are coins that have been in the gauge + // Can have multiple coin denoms + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` + // withdrawn_coins are coins that have been withdrawn by the stakeholder already + WithdrawnCoins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=withdrawn_coins,json=withdrawnCoins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"withdrawn_coins"` +} + +func (m *RewardGauge) Reset() { *m = RewardGauge{} } +func (m *RewardGauge) String() string { return proto.CompactTextString(m) } +func (*RewardGauge) ProtoMessage() {} +func (*RewardGauge) Descriptor() ([]byte, []int) { + return fileDescriptor_3954bc4942045a7a, []int{1} +} +func (m *RewardGauge) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RewardGauge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RewardGauge.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 *RewardGauge) XXX_Merge(src proto.Message) { + xxx_messageInfo_RewardGauge.Merge(m, src) +} +func (m *RewardGauge) XXX_Size() int { + return m.Size() +} +func (m *RewardGauge) XXX_DiscardUnknown() { + xxx_messageInfo_RewardGauge.DiscardUnknown(m) +} + +var xxx_messageInfo_RewardGauge proto.InternalMessageInfo + +func (m *RewardGauge) GetCoins() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Coins + } + return nil +} + +func (m *RewardGauge) GetWithdrawnCoins() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.WithdrawnCoins + } + return nil +} + +func init() { + proto.RegisterType((*Gauge)(nil), "babylon.incentive.Gauge") + proto.RegisterType((*RewardGauge)(nil), "babylon.incentive.RewardGauge") +} + +func init() { proto.RegisterFile("babylon/incentive/incentive.proto", fileDescriptor_3954bc4942045a7a) } + +var fileDescriptor_3954bc4942045a7a = []byte{ + // 269 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, 0xcf, 0xcc, 0x4b, 0x4e, 0xcd, 0x2b, 0xc9, 0x2c, 0x4b, 0x45, 0xb0, 0xf4, + 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0xa1, 0x4a, 0xf4, 0xe0, 0x12, 0x52, 0x22, 0xe9, 0xf9, + 0xe9, 0xf9, 0x60, 0x59, 0x7d, 0x10, 0x0b, 0xa2, 0x50, 0x4a, 0x2e, 0x39, 0xbf, 0x38, 0x37, 0xbf, + 0x58, 0x3f, 0x29, 0xb1, 0x38, 0x55, 0xbf, 0xcc, 0x30, 0x29, 0xb5, 0x24, 0xd1, 0x50, 0x3f, 0x39, + 0x3f, 0x33, 0x0f, 0x22, 0xaf, 0x94, 0xc5, 0xc5, 0xea, 0x9e, 0x58, 0x9a, 0x9e, 0x2a, 0x94, 0xc8, + 0xc5, 0x0a, 0x12, 0x2e, 0x96, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x92, 0xd4, 0x83, 0x68, 0xd4, + 0x03, 0x69, 0xd4, 0x83, 0x6a, 0xd4, 0x73, 0xce, 0xcf, 0xcc, 0x73, 0x32, 0x38, 0x71, 0x4f, 0x9e, + 0x61, 0xd5, 0x7d, 0x79, 0x8d, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, + 0xa8, 0x2d, 0x10, 0x4a, 0xb7, 0x38, 0x25, 0x5b, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x18, 0xac, 0xa1, + 0x38, 0x08, 0x62, 0xb2, 0xd2, 0x33, 0x46, 0x2e, 0xee, 0xa0, 0xd4, 0xf2, 0xc4, 0xa2, 0x14, 0x7a, + 0x59, 0x29, 0x54, 0xc2, 0xc5, 0x5f, 0x9e, 0x59, 0x92, 0x91, 0x52, 0x94, 0x58, 0x9e, 0x17, 0x0f, + 0xb1, 0x8c, 0x89, 0xfa, 0x96, 0xf1, 0xc1, 0xed, 0x00, 0xf3, 0x9d, 0xbc, 0x4f, 0x3c, 0x92, 0x63, + 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, + 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x10, 0xc9, 0x4c, 0x68, 0x14, 0x26, 0x67, 0x24, 0x66, + 0xe6, 0xc1, 0x38, 0xfa, 0x15, 0x48, 0x91, 0x0e, 0xb6, 0x22, 0x89, 0x0d, 0x1c, 0x51, 0xc6, 0x80, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x83, 0xc5, 0xfb, 0x16, 0x02, 0x00, 0x00, +} + +func (m *Gauge) 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 *Gauge) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Gauge) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RewardGauge) 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 *RewardGauge) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RewardGauge) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WithdrawnCoins) > 0 { + for iNdEx := len(m.WithdrawnCoins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.WithdrawnCoins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIncentive(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintIncentive(dAtA []byte, offset int, v uint64) int { + offset -= sovIncentive(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Gauge) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Coins) > 0 { + for _, e := range m.Coins { + l = e.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + } + return n +} + +func (m *RewardGauge) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Coins) > 0 { + for _, e := range m.Coins { + l = e.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + } + if len(m.WithdrawnCoins) > 0 { + for _, e := range m.WithdrawnCoins { + l = e.Size() + n += 1 + l + sovIncentive(uint64(l)) + } + } + return n +} + +func sovIncentive(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozIncentive(x uint64) (n int) { + return sovIncentive(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Gauge) 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 ErrIntOverflowIncentive + } + 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: Gauge: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Gauge: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIncentive(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIncentive + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RewardGauge) 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 ErrIntOverflowIncentive + } + 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: RewardGauge: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RewardGauge: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawnCoins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIncentive + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthIncentive + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIncentive + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WithdrawnCoins = append(m.WithdrawnCoins, types.Coin{}) + if err := m.WithdrawnCoins[len(m.WithdrawnCoins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIncentive(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIncentive + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipIncentive(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, ErrIntOverflowIncentive + } + 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, ErrIntOverflowIncentive + } + 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, ErrIntOverflowIncentive + } + 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, ErrInvalidLengthIncentive + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupIncentive + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthIncentive + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthIncentive = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIncentive = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupIncentive = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/incentive/types/keys.go b/x/incentive/types/keys.go new file mode 100644 index 000000000..83bb1ea32 --- /dev/null +++ b/x/incentive/types/keys.go @@ -0,0 +1,22 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "incentive" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_incentive" +) + +var ( + ParamsKey = []byte{0x01} // key prefix for the parameters + BTCStakingGaugeKey = []byte{0x02} // key prefix for BTC staking gauge at each height + BTCTimestampingGaugeKey = []byte{0x03} // key prefix for BTC timestamping gauge at each height + RewardGaugeKey = []byte{0x04} // key prefix for reward gauge for a given stakeholder in a given type +) diff --git a/x/incentive/types/mocked_keepers.go b/x/incentive/types/mocked_keepers.go new file mode 100644 index 000000000..833bbb50d --- /dev/null +++ b/x/incentive/types/mocked_keepers.go @@ -0,0 +1,181 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/incentive/types/expected_keepers.go + +// Package types is a generated GoMock package. +package types + +import ( + context "context" + reflect "reflect" + + types "github.com/babylonchain/babylon/x/epoching/types" + types0 "github.com/cosmos/cosmos-sdk/types" + gomock "github.com/golang/mock/gomock" +) + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetAccount mocks base method. +func (m *MockAccountKeeper) GetAccount(ctx context.Context, addr types0.AccAddress) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, addr) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) +} + +// GetModuleAccount mocks base method. +func (m *MockAccountKeeper) GetModuleAccount(ctx context.Context, name string) types0.ModuleAccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAccount", ctx, name) + ret0, _ := ret[0].(types0.ModuleAccountI) + return ret0 +} + +// GetModuleAccount indicates an expected call of GetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, name) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// GetAllBalances mocks base method. +func (m *MockBankKeeper) GetAllBalances(ctx context.Context, addr types0.AccAddress) types0.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr) + ret0, _ := ret[0].(types0.Coins) + return ret0 +} + +// GetAllBalances indicates an expected call of GetAllBalances. +func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr) +} + +// SendCoinsFromModuleToAccount mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types0.AccAddress, amt types0.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt) +} + +// SendCoinsFromModuleToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt types0.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", ctx, senderModule, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToModule indicates an expected call of SendCoinsFromModuleToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderModule, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), ctx, senderModule, recipientModule, amt) +} + +// SpendableCoins mocks base method. +func (m *MockBankKeeper) SpendableCoins(ctx context.Context, addr types0.AccAddress) types0.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr) + ret0, _ := ret[0].(types0.Coins) + return ret0 +} + +// SpendableCoins indicates an expected call of SpendableCoins. +func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) +} + +// MockEpochingKeeper is a mock of EpochingKeeper interface. +type MockEpochingKeeper struct { + ctrl *gomock.Controller + recorder *MockEpochingKeeperMockRecorder +} + +// MockEpochingKeeperMockRecorder is the mock recorder for MockEpochingKeeper. +type MockEpochingKeeperMockRecorder struct { + mock *MockEpochingKeeper +} + +// NewMockEpochingKeeper creates a new mock instance. +func NewMockEpochingKeeper(ctrl *gomock.Controller) *MockEpochingKeeper { + mock := &MockEpochingKeeper{ctrl: ctrl} + mock.recorder = &MockEpochingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEpochingKeeper) EXPECT() *MockEpochingKeeperMockRecorder { + return m.recorder +} + +// GetEpoch mocks base method. +func (m *MockEpochingKeeper) GetEpoch(ctx context.Context) *types.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpoch", ctx) + ret0, _ := ret[0].(*types.Epoch) + return ret0 +} + +// GetEpoch indicates an expected call of GetEpoch. +func (mr *MockEpochingKeeperMockRecorder) GetEpoch(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpoch", reflect.TypeOf((*MockEpochingKeeper)(nil).GetEpoch), ctx) +} diff --git a/x/incentive/types/msg.go b/x/incentive/types/msg.go new file mode 100644 index 000000000..a0c4caffa --- /dev/null +++ b/x/incentive/types/msg.go @@ -0,0 +1,11 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure that these message types implement the sdk.Msg interface +var ( + _ sdk.Msg = &MsgWithdrawReward{} + _ sdk.Msg = &MsgUpdateParams{} +) diff --git a/x/incentive/types/params.go b/x/incentive/types/params.go new file mode 100644 index 000000000..8173d4be2 --- /dev/null +++ b/x/incentive/types/params.go @@ -0,0 +1,76 @@ +package types + +import ( + "fmt" + + "cosmossdk.io/math" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "gopkg.in/yaml.v2" +) + +var _ paramtypes.ParamSet = (*Params)(nil) + +// ParamKeyTable the param key table for launch module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return Params{ + SubmitterPortion: math.LegacyNewDecWithPrec(5, 2), // 5 * 10^{-2} = 0.05 + ReporterPortion: math.LegacyNewDecWithPrec(5, 2), // 5 * 10^{-2} = 0.05 + BtcStakingPortion: math.LegacyNewDecWithPrec(2, 1), // 2 * 10^{-1} = 0.2 + } +} + +// ParamSetPairs get the params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{} +} + +// TotalPortion calculates the sum of portions of all stakeholders +func (p *Params) TotalPortion() math.LegacyDec { + sum := p.SubmitterPortion + sum = sum.Add(p.ReporterPortion) + sum = sum.Add(p.BtcStakingPortion) + return sum +} + +// BTCTimestampingPortion calculates the sum of portions of all BTC timestamping stakeholders +func (p *Params) BTCTimestampingPortion() math.LegacyDec { + sum := p.SubmitterPortion + sum = sum.Add(p.ReporterPortion) + return sum +} + +// BTCStakingPortion calculates the sum of portions of all BTC staking stakeholders +func (p *Params) BTCStakingPortion() math.LegacyDec { + return p.BtcStakingPortion +} + +// Validate validates the set of params +func (p Params) Validate() error { + if p.SubmitterPortion.IsNil() { + return fmt.Errorf("SubmitterPortion should not be nil") + } + if p.ReporterPortion.IsNil() { + return fmt.Errorf("ReporterPortion should not be nil") + } + if p.BtcStakingPortion.IsNil() { + return fmt.Errorf("BtcStakingPortion should not be nil") + } + + // sum of all portions should be less than 1 + if p.TotalPortion().GTE(math.LegacyOneDec()) { + return fmt.Errorf("sum of all portions should be less than 1") + } + + return nil +} + +// String implements the Stringer interface. +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} diff --git a/x/incentive/types/params.pb.go b/x/incentive/types/params.pb.go new file mode 100644 index 000000000..a0062b8d6 --- /dev/null +++ b/x/incentive/types/params.pb.go @@ -0,0 +1,424 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/incentive/params.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "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, including portions of rewards +// distributed to each type of stakeholder. Note that sum of the portions should +// be strictly less than 1 so that the rest will go to Comet validators/delegations +// adapted from https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/proto/cosmos/distribution/v1beta1/distribution.proto +type Params struct { + // submitter_portion is the portion of rewards that goes to submitter + SubmitterPortion cosmossdk_io_math.LegacyDec `protobuf:"bytes,1,opt,name=submitter_portion,json=submitterPortion,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"submitter_portion"` + // reporter_portion is the portion of rewards that goes to reporter + ReporterPortion cosmossdk_io_math.LegacyDec `protobuf:"bytes,2,opt,name=reporter_portion,json=reporterPortion,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"reporter_portion"` + // btc_staking_portion is the portion of rewards that goes to Finality Providers/delegations + // NOTE: the portion of each Finality Provider/delegation is calculated by using its voting + // power and finality provider's commission + BtcStakingPortion cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=btc_staking_portion,json=btcStakingPortion,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"btc_staking_portion"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_c42276168f0adf4b, []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 init() { + proto.RegisterType((*Params)(nil), "babylon.incentive.Params") +} + +func init() { proto.RegisterFile("babylon/incentive/params.proto", fileDescriptor_c42276168f0adf4b) } + +var fileDescriptor_c42276168f0adf4b = []byte{ + // 294 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0xcf, 0xcc, 0x4b, 0x4e, 0xcd, 0x2b, 0xc9, 0x2c, 0x4b, 0xd5, 0x2f, 0x48, + 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0xca, 0xeb, 0xc1, + 0xe5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x94, 0x64, + 0x72, 0x7e, 0x71, 0x6e, 0x7e, 0x71, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x48, 0x29, 0xad, 0x67, 0xe2, + 0x62, 0x0b, 0x00, 0x1b, 0x2a, 0x14, 0xc7, 0x25, 0x58, 0x5c, 0x9a, 0x94, 0x9b, 0x59, 0x52, 0x92, + 0x5a, 0x14, 0x5f, 0x90, 0x5f, 0x54, 0x92, 0x99, 0x9f, 0x27, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe9, + 0x64, 0x78, 0xe2, 0x9e, 0x3c, 0xc3, 0xad, 0x7b, 0xf2, 0xd2, 0x10, 0xbd, 0xc5, 0x29, 0xd9, 0x7a, + 0x99, 0xf9, 0xfa, 0xb9, 0x89, 0x25, 0x19, 0x7a, 0x3e, 0xa9, 0xe9, 0x89, 0xc9, 0x95, 0x2e, 0xa9, + 0xc9, 0x97, 0xb6, 0xe8, 0x72, 0x41, 0x8d, 0x76, 0x49, 0x4d, 0x0e, 0x12, 0x80, 0x9b, 0x15, 0x00, + 0x31, 0x4a, 0x28, 0x86, 0x4b, 0xa0, 0x28, 0x15, 0x64, 0x2e, 0x92, 0xf1, 0x4c, 0xe4, 0x1a, 0xcf, + 0x0f, 0x33, 0x0a, 0x66, 0x7a, 0x22, 0x97, 0x70, 0x52, 0x49, 0x72, 0x7c, 0x71, 0x49, 0x62, 0x76, + 0x66, 0x5e, 0x3a, 0xdc, 0x02, 0x66, 0x72, 0x2d, 0x10, 0x4c, 0x2a, 0x49, 0x0e, 0x86, 0x18, 0x06, + 0xb5, 0xc2, 0x8a, 0x65, 0xc6, 0x02, 0x79, 0x06, 0x27, 0xef, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, + 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, + 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4c, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, + 0x87, 0x46, 0x4d, 0x72, 0x46, 0x62, 0x66, 0x1e, 0x8c, 0xa3, 0x5f, 0x81, 0x14, 0x93, 0x25, 0x95, + 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0xe0, 0x58, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x55, 0x13, + 0x48, 0x09, 0xeb, 0x01, 0x00, 0x00, +} + +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 + { + size := m.BtcStakingPortion.Size() + i -= size + if _, err := m.BtcStakingPortion.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.ReporterPortion.Size() + i -= size + if _, err := m.ReporterPortion.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.SubmitterPortion.Size() + i -= size + if _, err := m.SubmitterPortion.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + 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 + l = m.SubmitterPortion.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.ReporterPortion.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.BtcStakingPortion.Size() + n += 1 + l + sovParams(uint64(l)) + 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubmitterPortion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SubmitterPortion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReporterPortion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ReporterPortion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcStakingPortion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BtcStakingPortion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + 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/incentive/types/query.pb.go b/x/incentive/types/query.pb.go new file mode 100644 index 000000000..bacf7178d --- /dev/null +++ b/x/incentive/types/query.pb.go @@ -0,0 +1,1815 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/incentive/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + 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. +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 + +// QueryParamsRequest is 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_e1a59cc0c7c44135, []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 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_e1a59cc0c7c44135, []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{} +} + +// QueryRewardGaugesRequest is request type for the Query/RewardGauges RPC method. +type QueryRewardGaugesRequest struct { + // address is the address of the stakeholder in bech32 string + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryRewardGaugesRequest) Reset() { *m = QueryRewardGaugesRequest{} } +func (m *QueryRewardGaugesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryRewardGaugesRequest) ProtoMessage() {} +func (*QueryRewardGaugesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{2} +} +func (m *QueryRewardGaugesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRewardGaugesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRewardGaugesRequest.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 *QueryRewardGaugesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRewardGaugesRequest.Merge(m, src) +} +func (m *QueryRewardGaugesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryRewardGaugesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRewardGaugesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRewardGaugesRequest proto.InternalMessageInfo + +func (m *QueryRewardGaugesRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// QueryRewardGaugesResponse is response type for the Query/RewardGauges RPC method. +type QueryRewardGaugesResponse struct { + // reward_gauges is the map of reward gauges, where key is the stakeholder type + // and value is the reward gauge holding all rewards for the stakeholder in that type + RewardGauges map[string]*RewardGauge `protobuf:"bytes,1,rep,name=reward_gauges,json=rewardGauges,proto3" json:"reward_gauges,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *QueryRewardGaugesResponse) Reset() { *m = QueryRewardGaugesResponse{} } +func (m *QueryRewardGaugesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryRewardGaugesResponse) ProtoMessage() {} +func (*QueryRewardGaugesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{3} +} +func (m *QueryRewardGaugesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRewardGaugesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRewardGaugesResponse.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 *QueryRewardGaugesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRewardGaugesResponse.Merge(m, src) +} +func (m *QueryRewardGaugesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryRewardGaugesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRewardGaugesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRewardGaugesResponse proto.InternalMessageInfo + +func (m *QueryRewardGaugesResponse) GetRewardGauges() map[string]*RewardGauge { + if m != nil { + return m.RewardGauges + } + return nil +} + +// QueryBTCStakingGaugeRequest is request type for the Query/BTCStakingGauge RPC method. +type QueryBTCStakingGaugeRequest struct { + // height is the queried Babylon height + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryBTCStakingGaugeRequest) Reset() { *m = QueryBTCStakingGaugeRequest{} } +func (m *QueryBTCStakingGaugeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBTCStakingGaugeRequest) ProtoMessage() {} +func (*QueryBTCStakingGaugeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{4} +} +func (m *QueryBTCStakingGaugeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCStakingGaugeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCStakingGaugeRequest.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 *QueryBTCStakingGaugeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCStakingGaugeRequest.Merge(m, src) +} +func (m *QueryBTCStakingGaugeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCStakingGaugeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCStakingGaugeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCStakingGaugeRequest proto.InternalMessageInfo + +func (m *QueryBTCStakingGaugeRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryBTCStakingGaugeResponse is response type for the Query/BTCStakingGauge RPC method. +type QueryBTCStakingGaugeResponse struct { + // gauge is the BTC staking gauge at the queried height + Gauge *Gauge `protobuf:"bytes,1,opt,name=gauge,proto3" json:"gauge,omitempty"` +} + +func (m *QueryBTCStakingGaugeResponse) Reset() { *m = QueryBTCStakingGaugeResponse{} } +func (m *QueryBTCStakingGaugeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBTCStakingGaugeResponse) ProtoMessage() {} +func (*QueryBTCStakingGaugeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{5} +} +func (m *QueryBTCStakingGaugeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCStakingGaugeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCStakingGaugeResponse.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 *QueryBTCStakingGaugeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCStakingGaugeResponse.Merge(m, src) +} +func (m *QueryBTCStakingGaugeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCStakingGaugeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCStakingGaugeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCStakingGaugeResponse proto.InternalMessageInfo + +func (m *QueryBTCStakingGaugeResponse) GetGauge() *Gauge { + if m != nil { + return m.Gauge + } + return nil +} + +// QueryBTCTimestampingGaugeRequest is request type for the Query/BTCTimestampingGauge RPC method. +type QueryBTCTimestampingGaugeRequest struct { + // epoch_num is the queried epoch number + EpochNum uint64 `protobuf:"varint,1,opt,name=epoch_num,json=epochNum,proto3" json:"epoch_num,omitempty"` +} + +func (m *QueryBTCTimestampingGaugeRequest) Reset() { *m = QueryBTCTimestampingGaugeRequest{} } +func (m *QueryBTCTimestampingGaugeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBTCTimestampingGaugeRequest) ProtoMessage() {} +func (*QueryBTCTimestampingGaugeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{6} +} +func (m *QueryBTCTimestampingGaugeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCTimestampingGaugeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCTimestampingGaugeRequest.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 *QueryBTCTimestampingGaugeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCTimestampingGaugeRequest.Merge(m, src) +} +func (m *QueryBTCTimestampingGaugeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCTimestampingGaugeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCTimestampingGaugeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCTimestampingGaugeRequest proto.InternalMessageInfo + +func (m *QueryBTCTimestampingGaugeRequest) GetEpochNum() uint64 { + if m != nil { + return m.EpochNum + } + return 0 +} + +// QueryBTCTimestampingGaugeResponse is response type for the Query/BTCTimestampingGauge RPC method. +type QueryBTCTimestampingGaugeResponse struct { + // gauge is the BTC timestamping gauge at the queried epoch + Gauge *Gauge `protobuf:"bytes,1,opt,name=gauge,proto3" json:"gauge,omitempty"` +} + +func (m *QueryBTCTimestampingGaugeResponse) Reset() { *m = QueryBTCTimestampingGaugeResponse{} } +func (m *QueryBTCTimestampingGaugeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBTCTimestampingGaugeResponse) ProtoMessage() {} +func (*QueryBTCTimestampingGaugeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e1a59cc0c7c44135, []int{7} +} +func (m *QueryBTCTimestampingGaugeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBTCTimestampingGaugeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBTCTimestampingGaugeResponse.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 *QueryBTCTimestampingGaugeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBTCTimestampingGaugeResponse.Merge(m, src) +} +func (m *QueryBTCTimestampingGaugeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBTCTimestampingGaugeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBTCTimestampingGaugeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBTCTimestampingGaugeResponse proto.InternalMessageInfo + +func (m *QueryBTCTimestampingGaugeResponse) GetGauge() *Gauge { + if m != nil { + return m.Gauge + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "babylon.incentive.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "babylon.incentive.QueryParamsResponse") + proto.RegisterType((*QueryRewardGaugesRequest)(nil), "babylon.incentive.QueryRewardGaugesRequest") + proto.RegisterType((*QueryRewardGaugesResponse)(nil), "babylon.incentive.QueryRewardGaugesResponse") + proto.RegisterMapType((map[string]*RewardGauge)(nil), "babylon.incentive.QueryRewardGaugesResponse.RewardGaugesEntry") + proto.RegisterType((*QueryBTCStakingGaugeRequest)(nil), "babylon.incentive.QueryBTCStakingGaugeRequest") + proto.RegisterType((*QueryBTCStakingGaugeResponse)(nil), "babylon.incentive.QueryBTCStakingGaugeResponse") + proto.RegisterType((*QueryBTCTimestampingGaugeRequest)(nil), "babylon.incentive.QueryBTCTimestampingGaugeRequest") + proto.RegisterType((*QueryBTCTimestampingGaugeResponse)(nil), "babylon.incentive.QueryBTCTimestampingGaugeResponse") +} + +func init() { proto.RegisterFile("babylon/incentive/query.proto", fileDescriptor_e1a59cc0c7c44135) } + +var fileDescriptor_e1a59cc0c7c44135 = []byte{ + // 615 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0xeb, 0x6e, 0x2d, 0xcc, 0x1b, 0x82, 0x99, 0x0a, 0x65, 0xd9, 0x08, 0x5d, 0x24, 0xa6, + 0x4a, 0xa0, 0x44, 0x74, 0x45, 0xa0, 0x09, 0x0d, 0x54, 0xfe, 0x49, 0x20, 0x55, 0x90, 0xed, 0xc4, + 0xa5, 0x72, 0x53, 0x2b, 0x8d, 0xd6, 0xc4, 0x59, 0xe2, 0x0c, 0xaa, 0xa9, 0x17, 0x2e, 0x5c, 0x91, + 0xf8, 0x24, 0x5c, 0xf8, 0x0c, 0xbb, 0x80, 0x26, 0x71, 0xe1, 0x84, 0xa0, 0xe5, 0x83, 0xa0, 0xda, + 0x6e, 0x95, 0xad, 0xe9, 0xda, 0xed, 0xe6, 0xfa, 0x7d, 0xfc, 0xbc, 0xbf, 0xd7, 0x79, 0x5c, 0x78, + 0xb3, 0x81, 0x1b, 0x9d, 0x36, 0xf5, 0x4d, 0xd7, 0xb7, 0x89, 0xcf, 0xdc, 0x03, 0x62, 0xee, 0xc7, + 0x24, 0xec, 0x18, 0x41, 0x48, 0x19, 0x45, 0xcb, 0xb2, 0x6c, 0x8c, 0xca, 0x6a, 0xc1, 0xa1, 0x0e, + 0xe5, 0x55, 0x73, 0xb0, 0x12, 0x42, 0x75, 0xcd, 0xa1, 0xd4, 0x69, 0x13, 0x13, 0x07, 0xae, 0x89, + 0x7d, 0x9f, 0x32, 0xcc, 0x5c, 0xea, 0x47, 0xb2, 0xaa, 0x8d, 0x77, 0x09, 0x70, 0x88, 0xbd, 0x61, + 0x7d, 0x7d, 0xbc, 0x3e, 0x5a, 0x09, 0x89, 0x5e, 0x80, 0xe8, 0xed, 0x00, 0xec, 0x0d, 0x3f, 0x67, + 0x91, 0xfd, 0x98, 0x44, 0x4c, 0xaf, 0xc1, 0xeb, 0x27, 0x76, 0xa3, 0x80, 0xfa, 0x11, 0x41, 0x0f, + 0x60, 0x5e, 0xf8, 0x2b, 0xa0, 0x08, 0x4a, 0x8b, 0xe5, 0x15, 0x63, 0x6c, 0x0e, 0x43, 0x1c, 0xa9, + 0xce, 0x1f, 0xfd, 0xbe, 0x95, 0xb1, 0xa4, 0x5c, 0xaf, 0x40, 0x85, 0xfb, 0x59, 0xe4, 0x3d, 0x0e, + 0x9b, 0x2f, 0x71, 0xec, 0x90, 0x61, 0x2f, 0xa4, 0xc0, 0x4b, 0xb8, 0xd9, 0x0c, 0x49, 0x24, 0x5c, + 0x17, 0xac, 0xe1, 0x4f, 0xfd, 0x2f, 0x80, 0x2b, 0x29, 0xc7, 0x24, 0x8c, 0x0d, 0xaf, 0x84, 0x7c, + 0xbf, 0xee, 0xf0, 0x82, 0x02, 0x8a, 0x73, 0xa5, 0xc5, 0xf2, 0x76, 0x0a, 0xd3, 0x44, 0x13, 0x23, + 0xb9, 0xf9, 0xdc, 0x67, 0x61, 0xc7, 0x5a, 0x0a, 0x13, 0x5b, 0x6a, 0x1d, 0x2e, 0x8f, 0x49, 0xd0, + 0x35, 0x38, 0xb7, 0x47, 0x3a, 0x92, 0x76, 0xb0, 0x44, 0x15, 0x98, 0x3b, 0xc0, 0xed, 0x98, 0x28, + 0x59, 0x7e, 0x2f, 0x5a, 0x0a, 0x43, 0xc2, 0xc6, 0x12, 0xe2, 0xad, 0xec, 0x43, 0xa0, 0xdf, 0x87, + 0xab, 0x9c, 0xae, 0xba, 0xfb, 0x74, 0x87, 0xe1, 0x3d, 0xd7, 0x77, 0x84, 0x44, 0x5e, 0xce, 0x0d, + 0x98, 0x6f, 0x11, 0xd7, 0x69, 0x31, 0xde, 0x6d, 0xde, 0x92, 0xbf, 0xf4, 0x1a, 0x5c, 0x4b, 0x3f, + 0x26, 0x2f, 0xc7, 0x80, 0x39, 0x7e, 0x2b, 0xf2, 0x43, 0x29, 0x29, 0x40, 0x12, 0x85, 0xcb, 0xf4, + 0xc7, 0xb0, 0x38, 0xf4, 0xdb, 0x75, 0x3d, 0x12, 0x31, 0xec, 0x05, 0xa7, 0x59, 0x56, 0xe1, 0x02, + 0x09, 0xa8, 0xdd, 0xaa, 0xfb, 0xb1, 0x27, 0x71, 0x2e, 0xf3, 0x8d, 0x5a, 0xec, 0xe9, 0x3b, 0x70, + 0xfd, 0x0c, 0x83, 0x8b, 0x51, 0x95, 0x7f, 0xe4, 0x60, 0x8e, 0xbb, 0xa2, 0x4f, 0x00, 0xe6, 0x45, + 0xb2, 0xd0, 0xed, 0x49, 0x1f, 0xf8, 0x44, 0x84, 0xd5, 0x8d, 0x69, 0x32, 0xc1, 0xa4, 0x1b, 0x1f, + 0x7f, 0xfe, 0xfb, 0x92, 0x2d, 0xa1, 0x0d, 0x53, 0xea, 0xed, 0x16, 0x76, 0x7d, 0x73, 0xd2, 0xcb, + 0x42, 0x5f, 0x01, 0x5c, 0x4a, 0x46, 0x02, 0xdd, 0x99, 0x2d, 0x70, 0x82, 0xea, 0xee, 0x79, 0xd2, + 0xa9, 0xbf, 0xe0, 0x6c, 0x4f, 0xd0, 0xf6, 0x34, 0x36, 0xf9, 0x62, 0xcc, 0x43, 0xb9, 0xe8, 0x9a, + 0xc9, 0xa7, 0x81, 0xbe, 0x01, 0x78, 0xf5, 0x54, 0x52, 0x90, 0x31, 0x89, 0x24, 0x3d, 0x89, 0xaa, + 0x39, 0xb3, 0x5e, 0xc2, 0x57, 0x39, 0xfc, 0x23, 0xb4, 0x35, 0x0d, 0xbe, 0xc1, 0xec, 0x7a, 0x24, + 0x1c, 0x04, 0xaf, 0x79, 0x28, 0x52, 0xde, 0x45, 0xdf, 0x01, 0x2c, 0xa4, 0x25, 0x0a, 0x6d, 0x9e, + 0x41, 0x33, 0x29, 0xc0, 0x6a, 0xe5, 0x7c, 0x87, 0xe4, 0x1c, 0xaf, 0xf8, 0x1c, 0xcf, 0x50, 0x75, + 0x96, 0x39, 0x58, 0xc2, 0x66, 0x38, 0xcc, 0xe8, 0xd1, 0x74, 0xab, 0xaf, 0x8f, 0x7a, 0x1a, 0x38, + 0xee, 0x69, 0xe0, 0x4f, 0x4f, 0x03, 0x9f, 0xfb, 0x5a, 0xe6, 0xb8, 0xaf, 0x65, 0x7e, 0xf5, 0xb5, + 0xcc, 0xbb, 0x7b, 0x8e, 0xcb, 0x5a, 0x71, 0xc3, 0xb0, 0xa9, 0x97, 0xde, 0xe7, 0x43, 0xa2, 0x13, + 0xeb, 0x04, 0x24, 0x6a, 0xe4, 0xf9, 0x3f, 0xf8, 0xe6, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, + 0x2f, 0x3f, 0xc6, 0x6c, 0x06, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// 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 { + // Parameters queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // RewardGauge queries the reward gauge of a given stakeholder address + RewardGauges(ctx context.Context, in *QueryRewardGaugesRequest, opts ...grpc.CallOption) (*QueryRewardGaugesResponse, error) + // BTCStakingGauge queries the BTC staking gauge of a given height + BTCStakingGauge(ctx context.Context, in *QueryBTCStakingGaugeRequest, opts ...grpc.CallOption) (*QueryBTCStakingGaugeResponse, error) + // BTCTimestampingGauge queries the BTC timestamping gauge of a given epoch + BTCTimestampingGauge(ctx context.Context, in *QueryBTCTimestampingGaugeRequest, opts ...grpc.CallOption) (*QueryBTCTimestampingGaugeResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +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.incentive.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) RewardGauges(ctx context.Context, in *QueryRewardGaugesRequest, opts ...grpc.CallOption) (*QueryRewardGaugesResponse, error) { + out := new(QueryRewardGaugesResponse) + err := c.cc.Invoke(ctx, "/babylon.incentive.Query/RewardGauges", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BTCStakingGauge(ctx context.Context, in *QueryBTCStakingGaugeRequest, opts ...grpc.CallOption) (*QueryBTCStakingGaugeResponse, error) { + out := new(QueryBTCStakingGaugeResponse) + err := c.cc.Invoke(ctx, "/babylon.incentive.Query/BTCStakingGauge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BTCTimestampingGauge(ctx context.Context, in *QueryBTCTimestampingGaugeRequest, opts ...grpc.CallOption) (*QueryBTCTimestampingGaugeResponse, error) { + out := new(QueryBTCTimestampingGaugeResponse) + err := c.cc.Invoke(ctx, "/babylon.incentive.Query/BTCTimestampingGauge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Parameters queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // RewardGauge queries the reward gauge of a given stakeholder address + RewardGauges(context.Context, *QueryRewardGaugesRequest) (*QueryRewardGaugesResponse, error) + // BTCStakingGauge queries the BTC staking gauge of a given height + BTCStakingGauge(context.Context, *QueryBTCStakingGaugeRequest) (*QueryBTCStakingGaugeResponse, error) + // BTCTimestampingGauge queries the BTC timestamping gauge of a given epoch + BTCTimestampingGauge(context.Context, *QueryBTCTimestampingGaugeRequest) (*QueryBTCTimestampingGaugeResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +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) RewardGauges(ctx context.Context, req *QueryRewardGaugesRequest) (*QueryRewardGaugesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RewardGauges not implemented") +} +func (*UnimplementedQueryServer) BTCStakingGauge(ctx context.Context, req *QueryBTCStakingGaugeRequest) (*QueryBTCStakingGaugeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BTCStakingGauge not implemented") +} +func (*UnimplementedQueryServer) BTCTimestampingGauge(ctx context.Context, req *QueryBTCTimestampingGaugeRequest) (*QueryBTCTimestampingGaugeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BTCTimestampingGauge not implemented") +} + +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.incentive.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_RewardGauges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryRewardGaugesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).RewardGauges(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.incentive.Query/RewardGauges", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).RewardGauges(ctx, req.(*QueryRewardGaugesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BTCStakingGauge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBTCStakingGaugeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BTCStakingGauge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.incentive.Query/BTCStakingGauge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BTCStakingGauge(ctx, req.(*QueryBTCStakingGaugeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BTCTimestampingGauge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBTCTimestampingGaugeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BTCTimestampingGauge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.incentive.Query/BTCTimestampingGauge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BTCTimestampingGauge(ctx, req.(*QueryBTCTimestampingGaugeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "babylon.incentive.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "RewardGauges", + Handler: _Query_RewardGauges_Handler, + }, + { + MethodName: "BTCStakingGauge", + Handler: _Query_BTCStakingGauge_Handler, + }, + { + MethodName: "BTCTimestampingGauge", + Handler: _Query_BTCTimestampingGauge_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/incentive/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 *QueryRewardGaugesRequest) 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 *QueryRewardGaugesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRewardGaugesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryRewardGaugesResponse) 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 *QueryRewardGaugesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRewardGaugesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RewardGauges) > 0 { + for k := range m.RewardGauges { + v := m.RewardGauges[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintQuery(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintQuery(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryBTCStakingGaugeRequest) 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 *QueryBTCStakingGaugeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCStakingGaugeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryBTCStakingGaugeResponse) 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 *QueryBTCStakingGaugeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCStakingGaugeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Gauge != nil { + { + size, err := m.Gauge.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 *QueryBTCTimestampingGaugeRequest) 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 *QueryBTCTimestampingGaugeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCTimestampingGaugeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EpochNum != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.EpochNum)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryBTCTimestampingGaugeResponse) 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 *QueryBTCTimestampingGaugeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBTCTimestampingGaugeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Gauge != nil { + { + size, err := m.Gauge.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 encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + 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 *QueryRewardGaugesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryRewardGaugesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.RewardGauges) > 0 { + for k, v := range m.RewardGauges { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovQuery(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovQuery(uint64(len(k))) + l + n += mapEntrySize + 1 + sovQuery(uint64(mapEntrySize)) + } + } + return n +} + +func (m *QueryBTCStakingGaugeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryBTCStakingGaugeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Gauge != nil { + l = m.Gauge.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBTCTimestampingGaugeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.EpochNum != 0 { + n += 1 + sovQuery(uint64(m.EpochNum)) + } + return n +} + +func (m *QueryBTCTimestampingGaugeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Gauge != nil { + l = m.Gauge.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +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 *QueryRewardGaugesRequest) 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: QueryRewardGaugesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRewardGaugesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + 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 *QueryRewardGaugesResponse) 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: QueryRewardGaugesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRewardGaugesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RewardGauges", 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 m.RewardGauges == nil { + m.RewardGauges = make(map[string]*RewardGauge) + } + var mapkey string + var mapvalue *RewardGauge + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthQuery + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthQuery + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthQuery + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthQuery + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &RewardGauge{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.RewardGauges[mapkey] = mapvalue + 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 *QueryBTCStakingGaugeRequest) 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: QueryBTCStakingGaugeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCStakingGaugeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryBTCStakingGaugeResponse) 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: QueryBTCStakingGaugeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCStakingGaugeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gauge", 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 m.Gauge == nil { + m.Gauge = &Gauge{} + } + if err := m.Gauge.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 *QueryBTCTimestampingGaugeRequest) 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: QueryBTCTimestampingGaugeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCTimestampingGaugeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochNum", wireType) + } + m.EpochNum = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EpochNum |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryBTCTimestampingGaugeResponse) 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: QueryBTCTimestampingGaugeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBTCTimestampingGaugeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gauge", 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 m.Gauge == nil { + m.Gauge = &Gauge{} + } + if err := m.Gauge.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 skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/incentive/types/query.pb.gw.go b/x/incentive/types/query.pb.gw.go new file mode 100644 index 000000000..5b8468f06 --- /dev/null +++ b/x/incentive/types/query.pb.gw.go @@ -0,0 +1,456 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: babylon/incentive/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +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_RewardGauges_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRewardGaugesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.RewardGauges(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_RewardGauges_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRewardGaugesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.RewardGauges(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BTCStakingGauge_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCStakingGaugeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.BTCStakingGauge(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BTCStakingGauge_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCStakingGaugeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.BTCStakingGauge(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BTCTimestampingGauge_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCTimestampingGaugeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["epoch_num"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "epoch_num") + } + + protoReq.EpochNum, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "epoch_num", err) + } + + msg, err := client.BTCTimestampingGauge(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BTCTimestampingGauge_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBTCTimestampingGaugeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["epoch_num"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "epoch_num") + } + + protoReq.EpochNum, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "epoch_num", err) + } + + msg, err := server.BTCTimestampingGauge(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// 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_RewardGauges_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_RewardGauges_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_RewardGauges_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCStakingGauge_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_BTCStakingGauge_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_BTCStakingGauge_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCTimestampingGauge_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_BTCTimestampingGauge_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_BTCTimestampingGauge_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "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_RewardGauges_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_RewardGauges_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_RewardGauges_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCStakingGauge_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_BTCStakingGauge_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_BTCStakingGauge_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BTCTimestampingGauge_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_BTCTimestampingGauge_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_BTCTimestampingGauge_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylonchain", "babylon", "incentive", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_RewardGauges_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"babylonchain", "babylon", "incentive", "address", "reward_gauge"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BTCStakingGauge_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylonchain", "babylon", "incentive", "btc_staking_gauge", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BTCTimestampingGauge_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"babylonchain", "babylon", "incentive", "btc_timestamping_gauge", "epoch_num"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_RewardGauges_0 = runtime.ForwardResponseMessage + + forward_Query_BTCStakingGauge_0 = runtime.ForwardResponseMessage + + forward_Query_BTCTimestampingGauge_0 = runtime.ForwardResponseMessage +) diff --git a/x/incentive/types/tx.pb.go b/x/incentive/types/tx.pb.go new file mode 100644 index 000000000..dece25fd3 --- /dev/null +++ b/x/incentive/types/tx.pb.go @@ -0,0 +1,1053 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/incentive/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "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. +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 + +// MsgWithdrawReward defines a message for withdrawing reward of a stakeholder. +type MsgWithdrawReward struct { + // {submitter, reporter, finality_provider, btc_delegation} + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // address is the address of the stakeholder in bech32 string + // signer of this msg has to be this address + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *MsgWithdrawReward) Reset() { *m = MsgWithdrawReward{} } +func (m *MsgWithdrawReward) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawReward) ProtoMessage() {} +func (*MsgWithdrawReward) Descriptor() ([]byte, []int) { + return fileDescriptor_b4de6776d39a3a22, []int{0} +} +func (m *MsgWithdrawReward) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawReward) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawReward.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 *MsgWithdrawReward) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawReward.Merge(m, src) +} +func (m *MsgWithdrawReward) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawReward) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawReward.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawReward proto.InternalMessageInfo + +func (m *MsgWithdrawReward) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *MsgWithdrawReward) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// MsgWithdrawRewardResponse is the response to the MsgWithdrawReward message +type MsgWithdrawRewardResponse struct { + // coins is the withdrawed coins + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` +} + +func (m *MsgWithdrawRewardResponse) Reset() { *m = MsgWithdrawRewardResponse{} } +func (m *MsgWithdrawRewardResponse) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawRewardResponse) ProtoMessage() {} +func (*MsgWithdrawRewardResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b4de6776d39a3a22, []int{1} +} +func (m *MsgWithdrawRewardResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawRewardResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawRewardResponse.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 *MsgWithdrawRewardResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawRewardResponse.Merge(m, src) +} +func (m *MsgWithdrawRewardResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawRewardResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawRewardResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawRewardResponse proto.InternalMessageInfo + +func (m *MsgWithdrawRewardResponse) GetCoins() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Coins + } + return nil +} + +// MsgUpdateParams defines a message for updating incentive 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 incentive 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_b4de6776d39a3a22, []int{2} +} +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_b4de6776d39a3a22, []int{3} +} +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((*MsgWithdrawReward)(nil), "babylon.incentive.MsgWithdrawReward") + proto.RegisterType((*MsgWithdrawRewardResponse)(nil), "babylon.incentive.MsgWithdrawRewardResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "babylon.incentive.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.incentive.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("babylon/incentive/tx.proto", fileDescriptor_b4de6776d39a3a22) } + +var fileDescriptor_b4de6776d39a3a22 = []byte{ + // 467 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4f, 0x6b, 0xd4, 0x40, + 0x1c, 0xdd, 0xb1, 0x7f, 0xa4, 0x63, 0xa9, 0x74, 0x28, 0x34, 0x9b, 0x43, 0x5a, 0x82, 0x87, 0x65, + 0xb1, 0x19, 0xb7, 0x82, 0x42, 0x6f, 0xc6, 0xa3, 0x2c, 0x4a, 0x44, 0x04, 0x0f, 0xca, 0x24, 0x19, + 0x26, 0x83, 0x66, 0x26, 0x64, 0xa6, 0xdb, 0xee, 0x45, 0xc4, 0x4f, 0x20, 0x7e, 0x0c, 0x4f, 0x3d, + 0xf8, 0x21, 0x7a, 0x2c, 0x3d, 0x79, 0x52, 0xd9, 0x3d, 0xf4, 0x6b, 0xc8, 0x64, 0x26, 0xed, 0xda, + 0x14, 0xf4, 0x34, 0xf3, 0xdb, 0xf7, 0xe6, 0xfd, 0xde, 0xef, 0xfd, 0x36, 0xd0, 0x4f, 0x49, 0x3a, + 0xfd, 0x20, 0x05, 0xe6, 0x22, 0xa3, 0x42, 0xf3, 0x09, 0xc5, 0xfa, 0x38, 0xaa, 0x6a, 0xa9, 0x25, + 0xda, 0x74, 0x58, 0x74, 0x89, 0xf9, 0x5b, 0x4c, 0x32, 0xd9, 0xa0, 0xd8, 0xdc, 0x2c, 0xd1, 0xef, + 0x67, 0x52, 0x95, 0x52, 0xbd, 0xb3, 0x80, 0x2d, 0x1c, 0xb4, 0x6d, 0x2b, 0x5c, 0x2a, 0x86, 0x27, + 0x23, 0x73, 0x38, 0x20, 0x70, 0x40, 0x4a, 0x14, 0xc5, 0x93, 0x51, 0x4a, 0x35, 0x19, 0xe1, 0x4c, + 0x72, 0xd1, 0xe2, 0x5d, 0x63, 0x15, 0xa9, 0x49, 0xe9, 0x84, 0xc3, 0xe7, 0x70, 0x73, 0xac, 0xd8, + 0x6b, 0xae, 0x8b, 0xbc, 0x26, 0x47, 0x09, 0x3d, 0x22, 0x75, 0x8e, 0x10, 0x5c, 0xd6, 0xd3, 0x8a, + 0x7a, 0x60, 0x17, 0x0c, 0xd6, 0x92, 0xe6, 0x8e, 0x3c, 0x78, 0x9b, 0xe4, 0x79, 0x4d, 0x95, 0xf2, + 0x6e, 0x35, 0x3f, 0xb7, 0xe5, 0xc1, 0xfa, 0xe7, 0x8b, 0x93, 0x61, 0x5b, 0x85, 0x1f, 0x61, 0xbf, + 0x23, 0x98, 0x50, 0x55, 0x49, 0xa1, 0x28, 0x22, 0x70, 0xc5, 0x78, 0x53, 0x1e, 0xd8, 0x5d, 0x1a, + 0xdc, 0xd9, 0xef, 0x47, 0x6e, 0x48, 0xe3, 0x3e, 0x72, 0xee, 0xa3, 0xa7, 0x92, 0x8b, 0xf8, 0xc1, + 0xe9, 0xcf, 0x9d, 0xde, 0xb7, 0x5f, 0x3b, 0x03, 0xc6, 0x75, 0x71, 0x98, 0x46, 0x99, 0x2c, 0x5d, + 0x22, 0xee, 0xd8, 0x53, 0xf9, 0x7b, 0x6c, 0x9c, 0xa9, 0xe6, 0x81, 0x4a, 0xac, 0x72, 0xf8, 0x15, + 0xc0, 0xbb, 0x63, 0xc5, 0x5e, 0x55, 0x39, 0xd1, 0xf4, 0x45, 0x33, 0x2a, 0x7a, 0x04, 0xd7, 0xc8, + 0xa1, 0x2e, 0x64, 0xcd, 0xf5, 0xd4, 0x0e, 0x15, 0x7b, 0xe7, 0xdf, 0xf7, 0xb6, 0x5c, 0xf7, 0x27, + 0xd6, 0xfa, 0x4b, 0x5d, 0x73, 0xc1, 0x92, 0x2b, 0x2a, 0x7a, 0x0c, 0x57, 0x6d, 0x58, 0xcd, 0xc8, + 0xc6, 0x6f, 0x67, 0x95, 0x91, 0x6d, 0x11, 0x2f, 0x1b, 0xbf, 0x89, 0xa3, 0x1f, 0x6c, 0x98, 0x48, + 0xae, 0x84, 0xc2, 0x3e, 0xdc, 0xbe, 0xe6, 0xa9, 0x8d, 0x64, 0xff, 0x1c, 0xc0, 0xa5, 0xb1, 0x62, + 0x28, 0x87, 0x1b, 0xd7, 0xb6, 0x70, 0xef, 0x86, 0x6e, 0x9d, 0x68, 0xfd, 0xfb, 0xff, 0xc3, 0xba, + 0x5c, 0xc0, 0x5b, 0xb8, 0xfe, 0x57, 0x32, 0xe1, 0xcd, 0xaf, 0x17, 0x39, 0xfe, 0xf0, 0xdf, 0x9c, + 0x56, 0xdf, 0x5f, 0xf9, 0x74, 0x71, 0x32, 0x04, 0xf1, 0xb3, 0xd3, 0x59, 0x00, 0xce, 0x66, 0x01, + 0xf8, 0x3d, 0x0b, 0xc0, 0x97, 0x79, 0xd0, 0x3b, 0x9b, 0x07, 0xbd, 0x1f, 0xf3, 0xa0, 0xf7, 0x66, + 0xb4, 0xb0, 0x4f, 0x27, 0x9b, 0x15, 0x84, 0x8b, 0xb6, 0xc0, 0xc7, 0x8b, 0x9f, 0x90, 0x59, 0x6f, + 0xba, 0xda, 0xfc, 0x53, 0x1f, 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, 0xc7, 0x73, 0x0d, 0x40, 0x64, + 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// 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 { + // WithdrawReward defines a method to withdraw rewards of a stakeholder + WithdrawReward(ctx context.Context, in *MsgWithdrawReward, opts ...grpc.CallOption) (*MsgWithdrawRewardResponse, error) + // UpdateParams updates the incentive module parameters. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) WithdrawReward(ctx context.Context, in *MsgWithdrawReward, opts ...grpc.CallOption) (*MsgWithdrawRewardResponse, error) { + out := new(MsgWithdrawRewardResponse) + err := c.cc.Invoke(ctx, "/babylon.incentive.Msg/WithdrawReward", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.incentive.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 { + // WithdrawReward defines a method to withdraw rewards of a stakeholder + WithdrawReward(context.Context, *MsgWithdrawReward) (*MsgWithdrawRewardResponse, error) + // UpdateParams updates the incentive module parameters. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) WithdrawReward(ctx context.Context, req *MsgWithdrawReward) (*MsgWithdrawRewardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawReward not implemented") +} +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_WithdrawReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWithdrawReward) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).WithdrawReward(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.incentive.Msg/WithdrawReward", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).WithdrawReward(ctx, req.(*MsgWithdrawReward)) + } + return interceptor(ctx, in, info, handler) +} + +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.incentive.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.incentive.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "WithdrawReward", + Handler: _Msg_WithdrawReward_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/incentive/tx.proto", +} + +func (m *MsgWithdrawReward) 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 *MsgWithdrawReward) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawReward) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintTx(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawRewardResponse) 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 *MsgWithdrawRewardResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawRewardResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +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 *MsgWithdrawReward) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgWithdrawRewardResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Coins) > 0 { + for _, e := range m.Coins { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +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 *MsgWithdrawReward) 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: MsgWithdrawReward: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawReward: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", 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.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", 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.Address = string(dAtA[iNdEx:postIndex]) + 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 *MsgWithdrawRewardResponse) 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: MsgWithdrawRewardResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawRewardResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", 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 + } + m.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-1].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 *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") +) diff --git a/x/incentive/types/types.go b/x/incentive/types/types.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/x/incentive/types/types.go @@ -0,0 +1 @@ +package types diff --git a/x/monitor/genesis.go b/x/monitor/genesis.go index 3056ff875..b786d9685 100644 --- a/x/monitor/genesis.go +++ b/x/monitor/genesis.go @@ -1,18 +1,18 @@ package monitor import ( + "context" "github.com/babylonchain/babylon/x/monitor/keeper" "github.com/babylonchain/babylon/x/monitor/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { } // ExportGenesis returns the capability module's exported genesis. -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() return genesis } diff --git a/x/monitor/genesis_test.go b/x/monitor/genesis_test.go index 11e9484a4..db95adb13 100644 --- a/x/monitor/genesis_test.go +++ b/x/monitor/genesis_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/babylonchain/babylon/x/monitor" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/stretchr/testify/require" simapp "github.com/babylonchain/babylon/app" @@ -13,7 +12,7 @@ import ( func TestExportGenesis(t *testing.T) { app := simapp.Setup(t, false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + ctx := app.BaseApp.NewContext(false) genesisState := monitor.ExportGenesis(ctx, app.MonitorKeeper) require.Equal(t, genesisState, types.DefaultGenesis()) } diff --git a/x/monitor/keeper/grpc_query_test.go b/x/monitor/keeper/grpc_query_test.go index c4775ddf7..6f33b8fe7 100644 --- a/x/monitor/keeper/grpc_query_test.go +++ b/x/monitor/keeper/grpc_query_test.go @@ -4,56 +4,48 @@ import ( "math/rand" "testing" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/btctxformatter" "github.com/babylonchain/babylon/testutil/datagen" "github.com/babylonchain/babylon/testutil/mocks" - btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" ckpttypes "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/babylonchain/babylon/x/epoching/testepoching" types2 "github.com/babylonchain/babylon/x/epoching/types" "github.com/babylonchain/babylon/x/monitor/types" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" ) func FuzzQueryEndedEpochBtcHeight(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - // a genesis validator is generated for setup - helper := testepoching.NewHelper(t) - lck := helper.App.BTCLightClientKeeper - mk := helper.App.MonitorKeeper - ek := helper.EpochingKeeper - queryHelper := baseapp.NewQueryServerTestHelper(helper.Ctx, helper.App.InterfaceRegistry()) + + babylonApp := app.Setup(t, false) + ctx := babylonApp.NewContext(false) + lck := babylonApp.BTCLightClientKeeper + mk := babylonApp.MonitorKeeper + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, babylonApp.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, mk) queryClient := types.NewQueryClient(queryHelper) - // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) - - // Insert header tree - tree := datagen.NewBTCHeaderTree() + // a genesis validator is generated for setup root := lck.GetBaseBTCHeader(ctx) - tree.Add(root, nil) - tree.GenRandomBTCHeaderTree(r, 1, 10, root, func(header *btclightclienttypes.BTCHeaderInfo) bool { - err := lck.InsertHeader(ctx, header.Header) - require.NoError(t, err) - return true - }) - - // EndBlock of block 1 - ctx = helper.EndBlock() + chain := datagen.GenRandomValidChainStartingFrom( + r, + 0, + root.Header.ToBlockHeader(), + nil, + 10, + ) + headerBytes := datagen.HeaderToHeaderBytes(chain) + err := lck.InsertHeaders(ctx, headerBytes) + require.NoError(t, err) // go to BeginBlock of block 11, and thus entering epoch 2 - for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { - ctx = helper.GenAndApplyEmptyBlock(r) - } - epoch = ek.GetEpoch(ctx) - require.Equal(t, uint64(2), epoch.EpochNumber) + mk.Hooks().AfterEpochEnds(ctx, 1) // query epoch 0 ended BTC light client height, should return base height req := types.QueryEndedEpochBtcHeightRequest{ @@ -77,34 +69,37 @@ func FuzzQueryReportedCheckpointBtcHeight(f *testing.F) { datagen.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) + // a genesis validator is generated for setup - helper := testepoching.NewHelper(t) ctl := gomock.NewController(t) defer ctl.Finish() - lck := helper.App.BTCLightClientKeeper - mk := helper.App.MonitorKeeper - ek := helper.EpochingKeeper - ck := helper.App.CheckpointingKeeper + + babylonApp := app.Setup(t, false) + ctx := babylonApp.NewContext(false) + lck := babylonApp.BTCLightClientKeeper + mk := babylonApp.MonitorKeeper + ck := babylonApp.CheckpointingKeeper mockEk := mocks.NewMockEpochingKeeper(ctl) ck.SetEpochingKeeper(mockEk) - queryHelper := baseapp.NewQueryServerTestHelper(helper.Ctx, helper.App.InterfaceRegistry()) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, babylonApp.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, mk) queryClient := types.NewQueryClient(queryHelper) // BeginBlock of block 1, and thus entering epoch 1 - ctx := helper.BeginBlock(r) - epoch := ek.GetEpoch(ctx) - require.Equal(t, uint64(1), epoch.EpochNumber) + mk.Hooks().AfterEpochEnds(ctx, 0) - // Insert header tree - tree := datagen.NewBTCHeaderTree() root := lck.GetBaseBTCHeader(ctx) - tree.Add(root, nil) - tree.GenRandomBTCHeaderTree(r, 1, 10, root, func(header *btclightclienttypes.BTCHeaderInfo) bool { - err := lck.InsertHeader(ctx, header.Header) - require.NoError(t, err) - return true - }) + chain := datagen.GenRandomValidChainStartingFrom( + r, + 0, + root.Header.ToBlockHeader(), + nil, + 10, + ) + headerBytes := datagen.HeaderToHeaderBytes(chain) + err := lck.InsertHeaders(ctx, headerBytes) + require.NoError(t, err) // Add checkpoint valBlsSet, privKeys := datagen.GenerateValidatorSetWithBLSPrivKeys(int(datagen.RandomIntOtherThan(r, 0, 10))) @@ -121,7 +116,7 @@ func FuzzQueryReportedCheckpointBtcHeight(f *testing.F) { mockEk.EXPECT().GetValidatorSet(gomock.Any(), gomock.Eq(mockCkptWithMeta.Ckpt.EpochNum)).Return(valSet).AnyTimes() // make sure voting power is always sufficient mockEk.EXPECT().GetTotalVotingPower(gomock.Any(), gomock.Eq(mockCkptWithMeta.Ckpt.EpochNum)).Return(int64(0)).AnyTimes() - err := ck.AddRawCheckpoint( + err = ck.AddRawCheckpoint( ctx, mockCkptWithMeta, ) @@ -130,7 +125,7 @@ func FuzzQueryReportedCheckpointBtcHeight(f *testing.F) { // Verify checkpoint btcCkpt := btctxformatter.RawBtcCheckpoint{ Epoch: mockCkptWithMeta.Ckpt.EpochNum, - LastCommitHash: *mockCkptWithMeta.Ckpt.LastCommitHash, + BlockHash: *mockCkptWithMeta.Ckpt.BlockHash, BitMap: mockCkptWithMeta.Ckpt.Bitmap, SubmitterAddress: datagen.GenRandomByteArray(r, btctxformatter.AddressLength), BlsSig: *mockCkptWithMeta.Ckpt.BlsMultiSig, diff --git a/x/monitor/keeper/hooks.go b/x/monitor/keeper/hooks.go index 49a24e0e7..c343cd254 100644 --- a/x/monitor/keeper/hooks.go +++ b/x/monitor/keeper/hooks.go @@ -1,12 +1,13 @@ package keeper import ( + "context" checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" etypes "github.com/babylonchain/babylon/x/epoching/types" sdk "github.com/cosmos/cosmos-sdk/types" ) -// Helper interface to be sure Hooks implement both epoching and light client hooks +// HandledHooks Helper interface to be sure Hooks implement both epoching and light client hooks type HandledHooks interface { etypes.EpochingHooks checkpointingtypes.CheckpointingHooks @@ -16,32 +17,32 @@ type Hooks struct { k Keeper } -// Create new distribution hooks +// Hooks Create new distribution hooks func (k Keeper) Hooks() Hooks { return Hooks{k} } -func (h Hooks) AfterEpochBegins(ctx sdk.Context, epoch uint64) {} +func (h Hooks) AfterEpochBegins(ctx context.Context, epoch uint64) {} -func (h Hooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) { +func (h Hooks) AfterEpochEnds(ctx context.Context, epoch uint64) { h.k.updateBtcLightClientHeightForEpoch(ctx, epoch) } -func (h Hooks) BeforeSlashThreshold(ctx sdk.Context, valSet etypes.ValidatorSet) {} +func (h Hooks) BeforeSlashThreshold(ctx context.Context, valSet etypes.ValidatorSet) {} -func (h Hooks) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error { +func (h Hooks) AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error { +func (h Hooks) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { return nil } -func (h Hooks) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +func (h Hooks) AfterRawCheckpointForgotten(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return h.k.removeCheckpointRecord(ctx, ckpt) } -func (h Hooks) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { +func (h Hooks) AfterRawCheckpointFinalized(ctx context.Context, epoch uint64) error { return nil } -func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return h.k.updateBtcLightClientHeightForCheckpoint(ctx, ckpt) } diff --git a/x/monitor/keeper/keeper.go b/x/monitor/keeper/keeper.go index aa5ff716a..9bd40ab8b 100644 --- a/x/monitor/keeper/keeper.go +++ b/x/monitor/keeper/keeper.go @@ -1,36 +1,34 @@ package keeper import ( + "context" + corestoretypes "cosmossdk.io/core/store" "fmt" ckpttypes "github.com/babylonchain/babylon/x/checkpointing/types" + "cosmossdk.io/log" "github.com/babylonchain/babylon/x/monitor/types" - "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) type ( Keeper struct { cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey + storeService corestoretypes.KVStoreService btcLightClientKeeper types.BTCLightClientKeeper } ) func NewKeeper( cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, bk types.BTCLightClientKeeper, ) Keeper { return Keeper{ cdc: cdc, - storeKey: storeKey, - memKey: memKey, + storeService: storeService, btcLightClientKeeper: bk, } } @@ -47,14 +45,16 @@ func bytesToUint64(bytes []byte) (uint64, error) { return sdk.BigEndianToUint64(bytes), nil } -func (k Keeper) updateBtcLightClientHeightForEpoch(ctx sdk.Context, epoch uint64) { - store := ctx.KVStore(k.storeKey) +func (k Keeper) updateBtcLightClientHeightForEpoch(ctx context.Context, epoch uint64) { + store := k.storeService.OpenKVStore(ctx) currentTipHeight := k.btcLightClientKeeper.GetTipInfo(ctx).Height - store.Set(types.GetEpochEndLightClientHeightKey(epoch), sdk.Uint64ToBigEndian(currentTipHeight)) + if err := store.Set(types.GetEpochEndLightClientHeightKey(epoch), sdk.Uint64ToBigEndian(currentTipHeight)); err != nil { + panic(err) + } } -func (k Keeper) updateBtcLightClientHeightForCheckpoint(ctx sdk.Context, ckpt *ckpttypes.RawCheckpoint) error { - store := ctx.KVStore(k.storeKey) +func (k Keeper) updateBtcLightClientHeightForCheckpoint(ctx context.Context, ckpt *ckpttypes.RawCheckpoint) error { + store := k.storeService.OpenKVStore(ctx) ckptHashStr := ckpt.HashStr() storeKey, err := types.GetCheckpointReportedLightClientHeightKey(ckptHashStr) @@ -62,21 +62,23 @@ func (k Keeper) updateBtcLightClientHeightForCheckpoint(ctx sdk.Context, ckpt *c return err } - // if the checkpoint exists, meaning an earlier checkpoint with a lower btc height is already recorded - // we should keep the lower btc height in the store - if store.Has(storeKey) { - k.Logger(ctx).With("module", fmt.Sprintf("checkpoint %s is already recorded", ckptHashStr)) + // if the checkpoint exists, meaning an earlier checkpoint with a lower BTC height is already recorded + // we should keep the lower BTC height in the store + has, err := store.Has(storeKey) + if err != nil { + panic(err) + } + if has { + k.Logger(sdk.UnwrapSDKContext(ctx)).With("module", fmt.Sprintf("checkpoint %s is already recorded", ckptHashStr)) return nil } currentTipHeight := k.btcLightClientKeeper.GetTipInfo(ctx).Height - store.Set(storeKey, sdk.Uint64ToBigEndian(currentTipHeight)) - - return nil + return store.Set(storeKey, sdk.Uint64ToBigEndian(currentTipHeight)) } -func (k Keeper) removeCheckpointRecord(ctx sdk.Context, ckpt *ckpttypes.RawCheckpoint) error { - store := ctx.KVStore(k.storeKey) +func (k Keeper) removeCheckpointRecord(ctx context.Context, ckpt *ckpttypes.RawCheckpoint) error { + store := k.storeService.OpenKVStore(ctx) ckptHashStr := ckpt.HashStr() storeKey, err := types.GetCheckpointReportedLightClientHeightKey(ckptHashStr) @@ -84,19 +86,23 @@ func (k Keeper) removeCheckpointRecord(ctx sdk.Context, ckpt *ckpttypes.RawCheck return err } - store.Delete(storeKey) - + if err := store.Delete(storeKey); err != nil { + panic(err) + } return nil } -func (k Keeper) LightclientHeightAtEpochEnd(ctx sdk.Context, epoch uint64) (uint64, error) { +func (k Keeper) LightclientHeightAtEpochEnd(ctx context.Context, epoch uint64) (uint64, error) { if epoch == 0 { return k.btcLightClientKeeper.GetBaseBTCHeader(ctx).Height, nil } - store := ctx.KVStore(k.storeKey) + store := k.storeService.OpenKVStore(ctx) - btcHeightBytes := store.Get(types.GetEpochEndLightClientHeightKey(epoch)) + btcHeightBytes, err := store.Get(types.GetEpochEndLightClientHeightKey(epoch)) + if err != nil { + panic(err) + } // nil would be returned if key does not exist if btcHeightBytes == nil { // we do not have any key under given epoch, most probably epoch did not finish @@ -113,15 +119,18 @@ func (k Keeper) LightclientHeightAtEpochEnd(ctx sdk.Context, epoch uint64) (uint return btcHeight, nil } -func (k Keeper) LightclientHeightAtCheckpointReported(ctx sdk.Context, hashString string) (uint64, error) { - store := ctx.KVStore(k.storeKey) +func (k Keeper) LightclientHeightAtCheckpointReported(ctx context.Context, hashString string) (uint64, error) { + store := k.storeService.OpenKVStore(ctx) storeKey, err := types.GetCheckpointReportedLightClientHeightKey(hashString) if err != nil { return 0, err } - btcHeightBytes := store.Get(storeKey) + btcHeightBytes, err := store.Get(storeKey) + if err != nil { + panic(err) + } // nil would be returned if key does not exist if btcHeightBytes == nil { return 0, types.ErrCheckpointNotReported.Wrapf("checkpoint hash: %s", hashString) diff --git a/x/monitor/module.go b/x/monitor/module.go index c1badb11c..b4674ff00 100644 --- a/x/monitor/module.go +++ b/x/monitor/module.go @@ -2,6 +2,7 @@ package monitor import ( "context" + "cosmossdk.io/core/appmodule" "encoding/json" "fmt" @@ -22,8 +23,10 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // ---------------------------------------------------------------------------- @@ -99,23 +102,16 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { type AppModule struct { AppModuleBasic - keeper keeper.Keeper - accountKeeper types.AccountKeeper - bankKeeper types.BankKeeper - // TODO: add dependencies to staking, slashing and evidence + keeper keeper.Keeper } func NewAppModule( cdc codec.Codec, keeper keeper.Keeper, - accountKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, ) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), keeper: keeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, } } @@ -138,14 +134,12 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the capability module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. @@ -158,10 +152,20 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(_ context.Context) error { + return nil +} // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. -func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - return []abci.ValidatorUpdate{} +func (am AppModule) EndBlock(_ context.Context) ([]abci.ValidatorUpdate, error) { + return []abci.ValidatorUpdate{}, nil +} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker } diff --git a/x/monitor/module_simulation.go b/x/monitor/module_simulation.go deleted file mode 100644 index 3c06916db..000000000 --- a/x/monitor/module_simulation.go +++ /dev/null @@ -1,45 +0,0 @@ -package monitor - -import ( - simappparams "github.com/babylonchain/babylon/app/params" - monitorsimulation "github.com/babylonchain/babylon/x/monitor/simulation" - "github.com/babylonchain/babylon/x/monitor/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = monitorsimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - accs := make([]string, len(simState.Accounts)) - for i, acc := range simState.Accounts { - accs[i] = acc.Address.String() - } - monitorgenesis := types.GenesisState{} - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&monitorgenesis) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - operations := make([]simtypes.WeightedOperation, 0) - - return operations -} diff --git a/x/monitor/simulation/simap.go b/x/monitor/simulation/simap.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/monitor/simulation/simap.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/monitor/types/errors.go b/x/monitor/types/errors.go index 85139bba8..d4a710f00 100644 --- a/x/monitor/types/errors.go +++ b/x/monitor/types/errors.go @@ -1,7 +1,5 @@ package types -// DONTCOVER - import ( errorsmod "cosmossdk.io/errors" ) diff --git a/x/monitor/types/expected_keepers.go b/x/monitor/types/expected_keepers.go index 6f3566003..0150c8dc2 100644 --- a/x/monitor/types/expected_keepers.go +++ b/x/monitor/types/expected_keepers.go @@ -1,24 +1,11 @@ package types import ( + "context" lc "github.com/babylonchain/babylon/x/btclightclient/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// AccountKeeper defines the expected account keeper used for simulations (noalias) -type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI - // Methods imported from account should be defined here -} - -// BankKeeper defines the expected interface needed to retrieve account balances. -type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here -} - type BTCLightClientKeeper interface { - GetTipInfo(ctx sdk.Context) *lc.BTCHeaderInfo - GetBaseBTCHeader(ctx sdk.Context) *lc.BTCHeaderInfo + GetTipInfo(ctx context.Context) *lc.BTCHeaderInfo + GetBaseBTCHeader(ctx context.Context) *lc.BTCHeaderInfo } diff --git a/x/monitor/types/genesis.go b/x/monitor/types/genesis.go index 094d1119c..5c5ec3cc2 100644 --- a/x/monitor/types/genesis.go +++ b/x/monitor/types/genesis.go @@ -1,8 +1,5 @@ package types -// DefaultIndex is the default capability global index -const DefaultIndex uint64 = 1 - // DefaultGenesis returns the default Capability genesis state func DefaultGenesis() *GenesisState { return &GenesisState{} diff --git a/x/zoneconcierge/README.md b/x/zoneconcierge/README.md new file mode 100644 index 000000000..e7040567b --- /dev/null +++ b/x/zoneconcierge/README.md @@ -0,0 +1,500 @@ +# ZoneConcierge + +The Zone Concierge module is responsible for generating BTC timestamps of +headers from other PoS blockchains. These BTC timestamps allow PoS blockchains +integrating with Babylon to achieve Bitcoin security, i.e., forking the PoS +blockchain is as hard as forking Bitcoin. The Zone Concierge module leverages +the IBC protocol to receive PoS blockchains' headers and provide them with +succint and provable information about their timestamps. + +There are two phases of integration for a PoS blockchain: + +- **Phase 1 integration:** Babylon receives PoS blockchain headers via standard + `MsgUpdateClient` messages in IBC light client protocol, timestamps them, and + functions as a canonical chain oracle for the PoS blockchain. + [Babylonscan](https://babylonscan.io/) shows PoS blockchains with phase 1 + integration. +- **Phase 2 integration:** In addition to phase 1, phase 2 allows a PoS + blockchain to receive BTC timestamps from Babylon via an IBC channel, such + that the PoS blockchain can use BTC timestamps to detect and resolve forks, as + well as other use cases such as Bitcoin-assisted fast unbonding. + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Concepts](#concepts) + - [Problem Statement](#problem-statement) + - [Design](#design) + - [Use cases](#use-cases) +- [State](#state) + - [Parameters](#parameters) + - [ChainInfo](#chaininfo) + - [EpochChainInfo](#epochchaininfo) + - [CanonicalChain](#canonicalchain) + - [Fork](#fork) + - [Params](#params) +- [PostHandler for intercepting IBC headers](#posthandler-for-intercepting-ibc-headers) +- [Hooks](#hooks) + - [Indexing headers upon `AfterEpochEnds`](#indexing-headers-upon-afterepochends) + - [Sending BTC timestamps upon `AfterRawCheckpointFinalized`](#sending-btc-timestamps-upon-afterrawcheckpointfinalized) +- [Interaction with PoS blockchains under phase 1 integration](#interaction-with-pos-blockchains-under-phase-1-integration) +- [Interaction with PoS blockchains under phase 2 integration](#interaction-with-pos-blockchains-under-phase-2-integration) +- [Messages and Queries](#messages-and-queries) + +## Concepts + +The Zone Concierge module is responsible for providing BTC timestamps of headers +from PoS blockchains connected to Babylon via the IBC protocol. +These BTC timestamps allow PoS blockchains to achieve Bitcoin security, i.e., +forking a PoS blockchain is as hard as forking Bitcoin. The Zone Concierge +module leverages the IBC light client protocol to receive headers with a valid +quorum certificate from PoS blockchains. These headers are then timestamped +together with the Babylon blockchain by Bitcoin, thereby achieving Bitcoin +security. The BTC timestamps can be propagated back to the PoS blockchains, such +that PoS blockchains can know their headers that have been checkpointed by +Bitcoin. + +### Problem Statement + +Babylon aims to provide Bitcoin security to other PoS blockchains. This involves +two functionalities: 1) checkpointing Babylon to Bitcoin, and 2) checkpointing +other PoS blockchains to Babylon. The {[Epoching](../epoching/), +[Checkpointing](../checkpointing/), [BTCCheckpoint](../btccheckpoint/), +[BTCLightclient](../btclightclient/)} modules jointly provide the functionality +of checkpointing Babylon to Bitcoin. The [Zone Concierge module](./) and the +[IBC modules](https://github.com/cosmos/ibc-go) jointly provide the +functionality of checkpointing PoS blockchains to Babylon. + +In order to checkpoint PoS blockchains to Babylon, Babylon needs to receive +headers of PoS blockchains and maintain all headers that have a *quorum +certificate* (a set of signatures from validators with > 2/3 total voting +power). Checkpointing canonical headers allows Babylon to act as a canonical +chain oracle. Checkpointing fork headers allows Babylon to identify dishonest +majority attacks. + +To summarize, the Zone Concierge module aims at providing the following +guarantees: + +- **Timestamping headers:** Babylon checkpoints PoS blockchains' (canonical and + fork) headers with a valid quorum certificate. +- **Verifiability of timestamps:** Babylon can provide a proof that a given + header is checkpointed by Bitcoin, where the proof is publicly verifiable + assuming access to a BTC light client. + +Under the following assumptions: + +- BTC is always secure with the [k-deep confirmation + rule](https://en.bitcoin.it/wiki/Confirmation); +- There exists >=1 honest IBC relayer and vigilante {submitter, reporter}; and +- The network is synchronous (i.e., messages are delivered within a known and + finite time bound). + +Note that the Bitcoin timestamping protocol uses Bitcoin as a single source of +truth, and does not make any assumption on the fraction of adversarial +validators in Babylon or PoS blockchains. That is, the above statement shall +hold even if Babylon and a PoS blockchain have dishonest supermajority. The +formal security analysis of the Bitcoin timestamping protocol can be found at +the Bitcoin timestamping [reseaarch paper](https://arxiv.org/pdf/2207.08392.pdf) +published at [S\&P'23](https://sp2023.ieee-security.org/). + +### Design + +The Zone Concierge module is responsible for checkpointing headers from PoS +blockchains and propagating succinct and verifiable information about them back +to the PoS blockchains. Specifically, the Zone Concierge module + +- leverages IBC light clients for checkpointing PoS blockchains; +- intercepts and indexes headers from PoS blockchains; and +- provides BTC timestamps proving that a header is checkpointed by Babylon and + Bitcoin (via queries or IBC packets). + +**Leveraging IBC light clients for checkpointing PoS blockchains.** Babylon +leverages the [IBC light client +protocol](https://github.com/cosmos/ibc/tree/main/spec/client/ics-007-tendermint-client) +to receive and verify headers of PoS blockchains. The IBC light client protocol +allows a blockchain `A` to maintain a *light client* of another blockchain `B`. +The light client contains a subset of headers in the ledger of blockchain `B`, +securing the following properties when blockchain `B` has more than 2/3 honest +voting power and there exists at least 1 honest IBC relayer. + +- **Safety:** The IBC light client in blockchain `A` is consistent with the + ledger of blockchain `B`. +- **Liveness:** The IBC light client in blockchain `A` keeps growing. + +Verifying a header is done by a special [quorum intersection +mechanism](https://arxiv.org/abs/2010.07031): upon a header from the relayer, +the light client checks whether the intersected voting power between the quorum +certificates of the current tip and the header is more than 1/3 of the voting +power in the current tip. If yes, then this ensures that there exists at least +one honest validator in the header's quorum certificate, and this header is +agreed by all honest validators. + +Babylon leverages the IBC light client protocol to checkpoint PoS blockchains to +itself. In particular, each header with a valid quorum certificate can be viewed +as a timestamp, and Babylon can generate an inclusion proof that a given header +of a PoS blockchain is committed to Babylon's `AppHash`. + +**Intercepting and Indexing Headers from PoS blockchains.** In order to further +checkpoint headers of PoS blockchains to Bitcoin, the Zone Concierge module +builds an index recording headers' positions on Babylon's ledger, which will +eventually be checkpointed by Bitcoin. To this end, the Zone Concierge module +intercepts headers from IBC light clients via a +[PostHandler](https://docs.cosmos.network/v0.50/learn/advanced/baseapp#runtx-antehandler-runmsgs-posthandler), +and indexes them. + +Note that the Zone Concierge module intercepts all headers that have a valid +quorum certificate, including canonical headers and fork headers. A fork header +with a valid quorum certificate is a signal of the dishonest majority attack: +the majority of validators are dishonest and sign conflicted headers. + +**Providing Proofs that a Header is Checkpointed by Bitcoin.** To support use +cases that need to verify BTC timestamps of headers, Zone Concierge can provide +proofs that the headers are indeed checkpointed to Bitcoin. The proof includes +the following: + +- `ProofCzHeaderInEpoch`: Proof that the header of the PoS blockchain is + included in an epoch of Babylon; +- `ProofEpochSealed`: Proof that the epoch has been agreed by > 2/3 voting power + of the validator set; and +- `ProofEpochSubmitted`: Proof that the epoch's checkpoint has been submitted to + Bitcoin. + +The first proof is formed as a Merkle proof that the IBC header is committed to +the `AppHash` after the epoch. The second proof is formed as a BLS +multi-signature jointly generated by the epoch's validator set. The last proof +is formed as Merkle proofs of two transactions that constitute a BTC checkpoint, +same as in [BTCCheckpoint module](../btccheckpoint/README.md). + +### Use cases + +The Bitcoin-checkpointed PoS blockchain will enable several applications, such +as raising alarms upon dishonest majority attacks and reducing the unbonding +period. These use cases require new plugins in the PoS blockchains, and will be +developed by Babylon team in the future. + +**Raising Alarms upon Dishonest Majority Attacks.** Zone Concierge timestamps +fork headers that have valid quorum certificates. Such fork header signals a +safety attack launched by the dishonest majority of validators. Babylon can send +the fork header back to the corresponding PoS blockchain, such that the PoS +blockchain will get notified with this dishonest majority attack, and can decide +to stall or initiate a social consensus. + +**Reducing Unbonding Period.** Zone Concierge provides a Bitcoin-checkpointed +prefix for a PoS blockchain. Such Bitcoin-checkpointed prefix resists against +the long range attacks, thus unbonding requests in this prefix can be safely +finished, leading to much shorter unbonding period compared to that in existing +PoS blockchains (e.g., 21 days in Cosmos SDK chains). + +## State + +The Zone Concierge module keeps handling IBC headers of PoS blockchains, and +maintains the following KV stores. + +### Parameters + +The [parameter storage](./keeper/params.go) maintains Zone Concierge module's +parameters. The Zone Concierge module's parameters are represented as a `Params` +[object](../../proto/babylon/zoneconcierge/v1/params.proto) defined as follows: + +```protobuf +// 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\"" ]; +} +``` + +### ChainInfo + +The [chain info storage](./keeper/chain_info_indexer.go) maintains `ChainInfo` +for each PoS blockchain. The key is the PoS blockchain's `ChainID`, and the +value is a `ChainInfo` object. The `ChainInfo` is a structure storing the +information of a PoS blockchain that checkpoints to Babylon. + +```protobuf +// ChainInfo is the information of a CZ +message ChainInfo { + // chain_id is the ID of the chain + string chain_id = 1; + // latest_header is the latest header in CZ's canonical chain + IndexedHeader latest_header = 2; + // latest_forks is the latest forks, formed as a series of IndexedHeader (from + // low to high) + Forks latest_forks = 3; + // timestamped_headers_count is the number of timestamped headers in CZ's + // canonical chain + uint64 timestamped_headers_count = 4; +} +``` + +### EpochChainInfo + +The [epoch chain info storage](./keeper/epoch_chain_info_indexer.go) maintains +`ChainInfo` at the end of each Babylon epoch for each PoS blockchain. The key is +the PoS blockchain's `ChainID` plus the epoch number, and the value is a +`ChainInfo` object. + +### CanonicalChain + +The [canonical chain storage](./keeper/canonical_chain_indexer.go) maintains the +metadata of canonical IBC headers of a PoS blockchain. The key is the consumer +chain's `ChainID` plus the height, and the value is a `IndexedHeader` object. +`IndexedHeader` is a structure storing IBC header's metadata. + +```protobuf +// IndexedHeader is the metadata of a CZ header +message IndexedHeader { + // chain_id is the unique ID of the chain + string chain_id = 1; + // hash is the hash of this header + bytes hash = 2; + // 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-finalized + google.protobuf.Timestamp time = 4 [ (gogoproto.stdtime) = true ]; + // babylon_header_hash is the hash of the babylon block that includes this CZ + // header + bytes babylon_header_hash = 5; + // babylon_header_height is the height of the babylon block that includes this CZ + // header + uint64 babylon_header_height = 6; + // epoch is the epoch number of this header on Babylon ledger + uint64 babylon_epoch = 7; + // 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 = 8; +} +``` + +### Fork + +The [fork storage](./keeper/fork_indexer.go) maintains the metadata of canonical +IBC headers of a PoS blockchain. The key is the PoS blockchain's `ChainID` plus +the height, and the value is a list of `IndexedHeader` objects, which represent +fork headers at that height. + +### Params + +The [parameter storage](./keeper/params.go) maintains the parameters for the +Zone Concierge module. + +```protobuf +// 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\"" ]; +} +``` + +## PostHandler for intercepting IBC headers + +The Zone Concierge module implements a +[PostHandler](https://docs.cosmos.network/v0.50/learn/advanced/baseapp#runtx-antehandler-runmsgs-posthandler) +`IBCHeaderDecorator` to intercept headers sent to the [IBC client +module](https://github.com/cosmos/ibc-go/tree/v8.0.0/modules/core/02-client). +The `IBCHeaderDecorator` PostHandler is defined at +[x/zoneconcierge/keeper/header_handler.go](./keeper/header_handler.go), and +works as follows. + +1. If the PoS blockchain hosting the header is not known to Babylon, initialize + `ChainInfo` storage for the PoS blockchain. +2. If the header is on a fork, insert the header to the fork storage and update + `ChainInfo`. +3. If the header is canonical, insert the header to the canonical chain storage + and update `ChainInfo`. + +## Hooks + +The Zone Concierge module subscribes to the Epoching module's `AfterEpochEnds` +[hook](../epoching/types/hooks.go) for indexing the epochs when receiving +headers from PoS blockchains, and the Checkpointing module's +`AfterRawCheckpointFinalized` [hook](../checkpointing/types/hooks.go) for phase +2 integration. + +### Indexing headers upon `AfterEpochEnds` + +The `AfterEpochEnds` hook is triggered upon an epoch is ended, i.e., the last +block in this epoch has been committed by CometBFT. Upon `AfterEpochEnds`, the +Zone Concierge will save the current `ChainInfo` to the `EpochChainInfo` storage +for each PoS blockchain. + +### Sending BTC timestamps upon `AfterRawCheckpointFinalized` + +The `AfterRawCheckpointFinalized` hook is triggered upon a checkpoint becoming +*finalized*, i.e., Bitcoin transactions of the checkpoint become `w`-deep in +Bitcoin's canonical chain, where `w` is the `checkpoint_finalization_timeout` +[parameter](../../proto/babylon/btccheckpoint/v1/params.proto) in the +[BTCCheckpoint](../btccheckpoint/) module. + +Upon `AfterRawCheckpointFinalized`, the Zone Concierge module will prepare and +send a BTC timestamp to each PoS blockchain. +The [BTCTimestamp](../../proto/babylon/zoneconcierge/v1/packet.proto) structure +includes a header and a set of proofs that the header is checkpointed by +Bitcoin. + + + +```protobuf +// BTCTimestamp is a BTC timestamp that carries information of a BTC-finalized 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 finalized epoch in Babylon, Babylon will send a BTC timestamp to each +// PoS blockchain 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 finalized 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; +} + +// ProofFinalizedChainInfo is a set of proofs that attest a chain info is +// BTC-finalized +message ProofFinalizedChainInfo { + /* + The following fields include proofs that attest the chain info is + BTC-finalized + */ + // proof_cz_header_in_epoch is the proof that the CZ header is timestamped + // within a certain epoch + tendermint.crypto.ProofOps proof_cz_header_in_epoch = 1; + // proof_epoch_sealed is the proof that the epoch is sealed + babylon.zoneconcierge.v1.ProofEpochSealed proof_epoch_sealed = 2; + // proof_epoch_submitted is the proof that the epoch's checkpoint is included + // in BTC ledger It is the two TransactionInfo in the best (i.e., earliest) + // checkpoint submission + repeated babylon.btccheckpoint.v1.TransactionInfo proof_epoch_submitted = 3; +} +``` + +Upon `AfterRawCheckpointFinalized` is triggered, the Zone Concierge module will +send an IBC packet including a `BTCTimestamp` to each PoS blockchain doing +[phase 2 +integration](#interaction-with-pos-blockchains-under-phase-2-integration) with +Babylon. The logic is defined at +[x/zoneconcierge/keeper/hooks.go](./keeper/hooks.go) and works as follows. + +1. Find all open IBC channels with Babylon's Zone Concierge module. The + counterparty at each IBC channel is a PoS blockchain. +2. Get all BTC headers to be sent in BTC timestamps. Specifically, + 1. Find the segment of BTC headers sent upon the last time + `AfterRawCheckpointFinalized` is triggered. + 2. If all BTC headers in the segment are no longer canonical, the BTC headers + to be sent will be the last `w+1` ones in the BTC light client, where `w` + is the `checkpoint_finalization_timeout` + [parameter](../../proto/babylon/btccheckpoint/v1/params.proto) in the + [BTCCheckpoint](../btccheckpoint/) module. + 3. Otherwise, the BTC headers to be sent will be from the latest header that + is still canonical in the segment to the current tip of the BTC light + client. +3. For each of these IBC channels: + 1. Find the `ChainID` of the counterparty chain (i.e., the PoS blockchain) in + the IBC channel. + 2. Get the `ChainInfo` of the `ChainID` at the last finalized epoch. + 3. Get the metadata of the last finalized epoch and its corresponding raw + checkpoint. + 4. Generate the proof that the last PoS blockchain's canonical header is + committed to the epoch's metadata. + 5. Generate the proof that the epoch is sealed, i.e., receives a BLS + multisignature generated by validators with >2/3 total voting power at the + last finalized epoch. + 6. Generate the proof that the epoch's checkpoint is submitted, i.e., encoded + in transactions on Bitcoin. + 7. Assemble all the above and the BTC headers obtained in step 2 as + `BTCTimestamp`, and send it to the IBC channel in an IBC packet. + +## Interaction with PoS blockchains under phase 1 integration + + + + +In phase 1 integration, Babylon maintains headers for a PoS blockchain via the +IBC light client protocol. The IBC light client of the PoS blockchain is +checkpointed by Bitcoin via Babylon, thus achieves Bitcoin security. + +Babylon utilizes the [IBC light client +protocol](https://github.com/cosmos/ibc/tree/main/spec/client/ics-007-tendermint-client) +for receiving headers from other PoS blockchains. The IBC headers are +encapsulated in the IBC protocol's `MsgUpdateClient` +[messages](https://github.com/cosmos/ibc-go/blob/v8.0.0/proto/ibc/core/client/v1/tx.proto#L20-L21), +and are sent to the [IBC client +module](https://github.com/cosmos/ibc-go/tree/v8.0.0/modules/core/02-client) by +an [IBC +relayer](https://github.com/cosmos/ibc/blob/main/spec/relayer/ics-018-relayer-algorithms/README.md). +The `IBCHeaderDecorator` PostHandler intercepts the headers and indexes their +positions in the `ChainInfo` storage, as per +[here](#indexing-headers-upon-afterepochends). This effectively checkpoints the +headers of PoS blockchains, completing the phase 1 integration. + +## Interaction with PoS blockchains under phase 2 integration + + + +In phase 2 integration, Babylon does everything in phase 1, and will send BTC +timestamps of headers back to each PoS blockchain. Each PoS blockchain can +verify the BTC timestamp and ensure that each header is finalized by Bitcoin, +thus obtaining Bitcoin security. The BTC timestamps can be used by the PoS +blockchain +for different use cases, e.g., BTC-assisted unbonding. + +The phase 2 integration does not require any change to the PoS blockchain's +code. Rather, it only needs to deploy a [Babylon +contract](https://github.com/babylonchain/babylon-contract) on the PoS +blockchain, and start an IBC relayer between Babylon and the Babylon contract on +the PoS blockchain. The Babylon contract can be deployed to a blockchain +supporting [CosmWasm](https://github.com/CosmWasm/cosmwasm) smart contracts, +connects with Babylon's Zone Concierge module via an IBC channel, and receives +BTC timestamps from Babylon to help the PoS blockchain get Bitcoin security. + +Upon a Babylon epoch becoming finalized, i.e., upon +`AfterRawCheckpointFinalized` is triggered, Babylon will send an IBC packet +including a `BTCTimestamp` to each PoS blockchain doing phase 2/3 integration +with Babylon, as per +[here](#sending-btc-timestamps-upon-afterrawcheckpointfinalized). + +Note that Zone Concierge provides 1-to-all connection, where the Zone Concierge +module establishes an IBC channel with each of multiple consumer chains. Zone +Concierge will send an BTC timestamp to each of these consumer chains upon an +epoch is finalized. + +## Messages and Queries + +The Zone Concierge module only has one message `MsgUpdateParams` for updating +the module parameters via a governance proposal. + +It provides a set of queries about the status of checkpointed PoS blockchains, +listed at +[docs.babylonchain.io](https://docs.babylonchain.io/docs/developer-guides/grpcrestapi#tag/ZoneConcierge). diff --git a/x/zoneconcierge/abci.go b/x/zoneconcierge/abci.go index 365c01ce7..7ea1514bb 100644 --- a/x/zoneconcierge/abci.go +++ b/x/zoneconcierge/abci.go @@ -1,23 +1,23 @@ package zoneconcierge import ( + "context" "time" "github.com/babylonchain/babylon/x/zoneconcierge/keeper" "github.com/babylonchain/babylon/x/zoneconcierge/types" abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" ) // BeginBlocker sends a pending packet for every channel upon each new block, // so that the relayer is kept awake to relay headers -func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) { +func BeginBlocker(ctx context.Context, k keeper.Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - + return nil } -func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { +func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) - return []abci.ValidatorUpdate{} + return []abci.ValidatorUpdate{}, nil } diff --git a/x/zoneconcierge/client/cli/query.go b/x/zoneconcierge/client/cli/query.go index 270ad106e..2b0c5ad9c 100644 --- a/x/zoneconcierge/client/cli/query.go +++ b/x/zoneconcierge/client/cli/query.go @@ -57,9 +57,11 @@ func CmdFinalizedChainsInfo() *cobra.Command { Short: "retrieve the finalized info for a given list of chains", Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { + prove, _ := cmd.Flags().GetBool("prove") + clientCtx := client.GetClientContextFromCmd(cmd) queryClient := types.NewQueryClient(clientCtx) - req := types.QueryFinalizedChainsInfoRequest{ChainIds: args} + req := types.QueryFinalizedChainsInfoRequest{ChainIds: args, Prove: prove} resp, err := queryClient.FinalizedChainsInfo(cmd.Context(), &req) if err != nil { return err @@ -69,7 +71,9 @@ func CmdFinalizedChainsInfo() *cobra.Command { }, } + cmd.Flags().Bool("prove", false, "whether to retrieve proofs for each FinalizedChainInfo") flags.AddQueryFlagsToCmd(cmd) + return cmd } diff --git a/x/zoneconcierge/client/cli/tx.go b/x/zoneconcierge/client/cli/tx.go index 1e0d535f4..9ba77b787 100644 --- a/x/zoneconcierge/client/cli/tx.go +++ b/x/zoneconcierge/client/cli/tx.go @@ -2,8 +2,6 @@ package cli import ( "fmt" - "time" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -11,16 +9,6 @@ import ( "github.com/babylonchain/babylon/x/zoneconcierge/types" ) -var ( - DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) -) - -//nolint:unused -const ( - flagPacketTimeoutTimestamp = "packet-timeout-timestamp" - listSeparator = "," -) - // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/zoneconcierge/extended-client-keeper/hooks.go b/x/zoneconcierge/extended-client-keeper/hooks.go deleted file mode 100644 index 516e7ecac..000000000 --- a/x/zoneconcierge/extended-client-keeper/hooks.go +++ /dev/null @@ -1,45 +0,0 @@ -package extended_client_keeper - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ClientHooks defines the hook interface for client -type ClientHooks interface { - AfterHeaderWithValidCommit(ctx sdk.Context, txHash []byte, header *HeaderInfo, isOnFork bool) -} - -type HeaderInfo struct { - Hash []byte - ChaindId string - Height uint64 - Time time.Time -} - -// MultiClientHooks is a concrete implementation of ClientHooks -// It allows other modules to hook onto client ExtendedKeeper -var _ ClientHooks = &MultiClientHooks{} - -type MultiClientHooks []ClientHooks - -func NewMultiClientHooks(hooks ...ClientHooks) MultiClientHooks { - return hooks -} - -// invoke hooks in each keeper that hooks onto ExtendedKeeper -func (h MultiClientHooks) AfterHeaderWithValidCommit(ctx sdk.Context, txHash []byte, header *HeaderInfo, isOnFork bool) { - for i := range h { - h[i].AfterHeaderWithValidCommit(ctx, txHash, header, isOnFork) - } -} - -// ensure ExtendedKeeper implements ClientHooks interfaces -var _ ClientHooks = ExtendedKeeper{} - -func (ek ExtendedKeeper) AfterHeaderWithValidCommit(ctx sdk.Context, txHash []byte, header *HeaderInfo, isOnFork bool) { - if ek.hooks != nil { - ek.hooks.AfterHeaderWithValidCommit(ctx, txHash, header, isOnFork) - } -} diff --git a/x/zoneconcierge/extended-client-keeper/keeper.go b/x/zoneconcierge/extended-client-keeper/keeper.go deleted file mode 100644 index 68474423c..000000000 --- a/x/zoneconcierge/extended-client-keeper/keeper.go +++ /dev/null @@ -1,150 +0,0 @@ -package extended_client_keeper - -import ( - sdkerrors "cosmossdk.io/errors" - metrics "github.com/armon/go-metrics" - "github.com/cometbft/cometbft/crypto/tmhash" - "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" -) - -// ExtendedKeeper is same as the original clientkeeper.Keeper, except that -// - it provides hooks for notifying other modules on received headers -// - it applies different verification rules on received headers -// (notably, intercepting headers rather than freezing clients upon errors that indicate dishonest majority) -type ExtendedKeeper struct { - clientkeeper.Keeper - cdc codec.BinaryCodec // since some code needs to use k.cdc - hooks ClientHooks -} - -// GetHeaderInfo returns the information necessary for header timestamping or nil -// if provided message is not a header -func GetHeaderInfo(ctx sdk.Context, m exported.ClientMessage) *HeaderInfo { - switch msg := m.(type) { - case *ibctmtypes.Header: - return &HeaderInfo{ - Hash: msg.Header.LastCommitHash, - ChaindId: msg.Header.ChainID, - Height: uint64(msg.Header.Height), - Time: msg.Header.Time, - } - default: - return nil - } -} - -// NewExtendedKeeper creates a new NewExtendedKeeper instance -func NewExtendedKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace, sk types.StakingKeeper, uk types.UpgradeKeeper) ExtendedKeeper { - // set KeyTable if it has not already been set - if !paramSpace.HasKeyTable() { - paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) - } - - k := clientkeeper.NewKeeper(cdc, key, paramSpace, sk, uk) - return ExtendedKeeper{ - Keeper: k, - cdc: cdc, - hooks: nil, - } -} - -// SetHooks sets the hooks for ExtendedKeeper -func (ek *ExtendedKeeper) SetHooks(ch ClientHooks) *ExtendedKeeper { - if ek.hooks != nil { - panic("cannot set hooks twice") - } - ek.hooks = ch - - return ek -} - -// UpdateClient updates the consensus state and the state root from a provided header. -// The implementation is the same as the original IBC-Go implementation, except from: -// 1. Not freezing the client when finding a misbehaviour for header message -// 2. Calling a AfterHeaderWithValidCommit callback when receiving valid header messages (either misbehaving or not) -func (k ExtendedKeeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { - // In case of nil message nothing changes in comparison to the original IBC-Go implementation - if clientMsg == nil { - return k.Keeper.UpdateClient(ctx, clientID, clientMsg) - } - - clientState, found := k.GetClientState(ctx, clientID) - if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) - } - - clientStore := k.ClientStore(ctx, clientID) - - if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) - } - - if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { - return err - } - - foundMisbehaviour := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, clientMsg) - - headerInfo := GetHeaderInfo(ctx, clientMsg) - - // found misbehaviour and it was not an header, freeze client - if foundMisbehaviour && headerInfo == nil { - clientState.UpdateStateOnMisbehaviour(ctx, k.cdc, clientStore, clientMsg) - - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelMsgType, "update"), - }, - ) - - clientkeeper.EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) - - return nil - } else if foundMisbehaviour && headerInfo != nil { - // found misbehaviour and it was an header, this is most probably means - // conflicting headers misbehaviour. - ctx.Logger().Debug("received a header that has QC but is on a fork") - txHash := tmhash.Sum(ctx.TxBytes()) - k.AfterHeaderWithValidCommit(ctx, txHash, headerInfo, true) - return nil - } - - // there was no misbehaviour and we receivied an header, call the callback - if headerInfo != nil { - txHash := tmhash.Sum(ctx.TxBytes()) // get hash of the tx that includes this header - k.AfterHeaderWithValidCommit(ctx, txHash, headerInfo, false) - } - - consensusHeights := clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) - - k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) - - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "update"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelUpdateType, "msg"), - }, - ) - - // emitting events in the keeper emits for both begin block and handler client updates - clientkeeper.EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, k.cdc, clientMsg) - - return nil -} diff --git a/x/zoneconcierge/genesis.go b/x/zoneconcierge/genesis.go index eb5ec4424..6cdc963bf 100644 --- a/x/zoneconcierge/genesis.go +++ b/x/zoneconcierge/genesis.go @@ -1,13 +1,15 @@ package zoneconcierge import ( + "context" "github.com/babylonchain/babylon/x/zoneconcierge/keeper" "github.com/babylonchain/babylon/x/zoneconcierge/types" sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the module's state from a provided genesis state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { +func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + sdkCtx := sdk.UnwrapSDKContext(ctx) // set params for this module if err := k.SetParams(ctx, genState.Params); err != nil { panic(err) @@ -16,10 +18,10 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) 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 - if !k.IsBound(ctx, genState.PortId) { + if !k.IsBound(sdkCtx, genState.PortId) { // module binds to the port on InitChain // and claims the returned capability - err := k.BindPort(ctx, genState.PortId) + err := k.BindPort(sdkCtx, genState.PortId) if err != nil { panic("could not claim port capability: " + err.Error()) } @@ -27,7 +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 { +func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() genesis.Params = k.GetParams(ctx) genesis.PortId = k.GetPort(ctx) diff --git a/x/zoneconcierge/genesis_test.go b/x/zoneconcierge/genesis_test.go index 04e049233..59ae3107d 100644 --- a/x/zoneconcierge/genesis_test.go +++ b/x/zoneconcierge/genesis_test.go @@ -16,7 +16,7 @@ func TestGenesis(t *testing.T) { Params: types.Params{IbcPacketTimeoutSeconds: 100}, } - k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) + k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil) zoneconcierge.InitGenesis(ctx, *k, genesisState) got := zoneconcierge.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/zoneconcierge/keeper/canonical_chain_indexer.go b/x/zoneconcierge/keeper/canonical_chain_indexer.go index 860e53008..2348ba387 100644 --- a/x/zoneconcierge/keeper/canonical_chain_indexer.go +++ b/x/zoneconcierge/keeper/canonical_chain_indexer.go @@ -1,16 +1,18 @@ package keeper import ( + "context" "fmt" + "github.com/cosmos/cosmos-sdk/runtime" sdkerrors "cosmossdk.io/errors" + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/zoneconcierge/types" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) // FindClosestHeader finds the IndexedHeader that is closest to (but not after) the given height -func (k Keeper) FindClosestHeader(ctx sdk.Context, chainID string, height uint64) (*types.IndexedHeader, error) { +func (k Keeper) FindClosestHeader(ctx context.Context, chainID string, height uint64) (*types.IndexedHeader, error) { chainInfo, err := k.GetChainInfo(ctx, chainID) if err != nil { return nil, fmt.Errorf("failed to get chain info for chain with ID %s: %w", chainID, err) @@ -37,7 +39,7 @@ func (k Keeper) FindClosestHeader(ctx sdk.Context, chainID string, height uint64 return &header, nil } -func (k Keeper) GetHeader(ctx sdk.Context, chainID string, height uint64) (*types.IndexedHeader, error) { +func (k Keeper) GetHeader(ctx context.Context, chainID string, height uint64) (*types.IndexedHeader, error) { store := k.canonicalChainStore(ctx, chainID) heightBytes := sdk.Uint64ToBigEndian(height) if !store.Has(heightBytes) { @@ -49,7 +51,7 @@ func (k Keeper) GetHeader(ctx sdk.Context, chainID string, height uint64) (*type return &header, nil } -func (k Keeper) insertHeader(ctx sdk.Context, chainID string, header *types.IndexedHeader) error { +func (k Keeper) insertHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error { if header == nil { return sdkerrors.Wrapf(types.ErrInvalidHeader, "header is nil") } @@ -63,9 +65,9 @@ func (k Keeper) insertHeader(ctx sdk.Context, chainID string, header *types.Inde // prefix: CanonicalChainKey || chainID // key: height // value: IndexedHeader -func (k Keeper) canonicalChainStore(ctx sdk.Context, chainID string) prefix.Store { - store := ctx.KVStore(k.storeKey) - canonicalChainStore := prefix.NewStore(store, types.CanonicalChainKey) +func (k Keeper) canonicalChainStore(ctx context.Context, chainID string) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + canonicalChainStore := prefix.NewStore(storeAdapter, types.CanonicalChainKey) chainIDBytes := []byte(chainID) return prefix.NewStore(canonicalChainStore, chainIDBytes) } diff --git a/x/zoneconcierge/keeper/canonical_chain_indexer_test.go b/x/zoneconcierge/keeper/canonical_chain_indexer_test.go index 365854120..1b1f929ff 100644 --- a/x/zoneconcierge/keeper/canonical_chain_indexer_test.go +++ b/x/zoneconcierge/keeper/canonical_chain_indexer_test.go @@ -4,6 +4,7 @@ import ( "math/rand" "testing" + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/testutil/datagen" "github.com/stretchr/testify/require" ) @@ -14,33 +15,32 @@ func FuzzCanonicalChainIndexer(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" // simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 - headers := SimulateHeadersViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders) + headers := SimulateNewHeaders(ctx, r, &zcKeeper, czChainID, 0, numHeaders) // check if the canonical chain index is correct or not for i := uint64(0); i < numHeaders; i++ { - header, err := zcKeeper.GetHeader(ctx, czChain.ChainID, i) + header, err := zcKeeper.GetHeader(ctx, czChainID, i) require.NoError(t, err) require.NotNil(t, header) - require.Equal(t, czChain.ChainID, header.ChainId) + require.Equal(t, czChainID, header.ChainId) require.Equal(t, i, header.Height) - require.Equal(t, headers[i].Header.LastCommitHash, header.Hash) + require.Equal(t, headers[i].Header.AppHash, header.Hash) } // check if the chain info is updated or not - chainInfo, err := zcKeeper.GetChainInfo(ctx, czChain.ChainID) + chainInfo, err := zcKeeper.GetChainInfo(ctx, czChainID) require.NoError(t, err) require.NotNil(t, chainInfo.LatestHeader) - require.Equal(t, czChain.ChainID, chainInfo.LatestHeader.ChainId) + require.Equal(t, czChainID, chainInfo.LatestHeader.ChainId) require.Equal(t, numHeaders-1, chainInfo.LatestHeader.Height) - require.Equal(t, headers[numHeaders-1].Header.LastCommitHash, chainInfo.LatestHeader.Hash) + require.Equal(t, headers[numHeaders-1].Header.AppHash, chainInfo.LatestHeader.Hash) }) } @@ -50,37 +50,36 @@ func FuzzFindClosestHeader(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" // no header at the moment, FindClosestHeader invocation should give error - _, err := zcKeeper.FindClosestHeader(ctx, czChain.ChainID, 100) + _, err := zcKeeper.FindClosestHeader(ctx, czChainID, 100) require.Error(t, err) // simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 - headers := SimulateHeadersViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders) + headers := SimulateNewHeaders(ctx, r, &zcKeeper, czChainID, 0, numHeaders) - header, err := zcKeeper.FindClosestHeader(ctx, czChain.ChainID, numHeaders) + header, err := zcKeeper.FindClosestHeader(ctx, czChainID, numHeaders) require.NoError(t, err) - require.Equal(t, headers[len(headers)-1].Header.LastCommitHash, header.Hash) + require.Equal(t, headers[len(headers)-1].Header.AppHash, header.Hash) // skip a non-zero number of headers in between, in order to create a gap of non-timestamped headers gap := datagen.RandomInt(r, 10) + 1 // simulate a random number of blocks // where the new batch of headers has a gap with the previous batch - SimulateHeadersViaHook(ctx, r, hooks, czChain.ChainID, numHeaders+gap+1, numHeaders) + SimulateNewHeaders(ctx, r, &zcKeeper, czChainID, numHeaders+gap+1, numHeaders) // get a random height that is in this gap randomHeightInGap := datagen.RandomInt(r, int(gap+1)) + numHeaders // find the closest header with the given randomHeightInGap - header, err = zcKeeper.FindClosestHeader(ctx, czChain.ChainID, randomHeightInGap) + header, err = zcKeeper.FindClosestHeader(ctx, czChainID, randomHeightInGap) require.NoError(t, err) // the header should be the same as the last header in the last batch - require.Equal(t, headers[len(headers)-1].Header.LastCommitHash, header.Hash) + require.Equal(t, headers[len(headers)-1].Header.AppHash, header.Hash) }) } diff --git a/x/zoneconcierge/keeper/chain_info_indexer.go b/x/zoneconcierge/keeper/chain_info_indexer.go index cd71c2904..e7e7d06bd 100644 --- a/x/zoneconcierge/keeper/chain_info_indexer.go +++ b/x/zoneconcierge/keeper/chain_info_indexer.go @@ -1,21 +1,21 @@ package keeper import ( + "context" "fmt" + "github.com/cosmos/cosmos-sdk/runtime" errorsmod "cosmossdk.io/errors" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/zoneconcierge/types" ) -func (k Keeper) setChainInfo(ctx sdk.Context, chainInfo *types.ChainInfo) { +func (k Keeper) setChainInfo(ctx context.Context, chainInfo *types.ChainInfo) { store := k.chainInfoStore(ctx) store.Set([]byte(chainInfo.ChainId), k.cdc.MustMarshal(chainInfo)) } -func (k Keeper) InitChainInfo(ctx sdk.Context, chainID string) (*types.ChainInfo, error) { +func (k Keeper) InitChainInfo(ctx context.Context, chainID string) (*types.ChainInfo, error) { if len(chainID) == 0 { return nil, fmt.Errorf("chainID is empty") } @@ -40,7 +40,7 @@ func (k Keeper) InitChainInfo(ctx sdk.Context, chainID string) (*types.ChainInfo // HasChainInfo returns whether the chain info exists for a given ID // Since IBC does not provide API that allows to initialise chain info right before creating an IBC connection, // we can only check its existence every time, and return an empty one if it's not initialised yet. -func (k Keeper) HasChainInfo(ctx sdk.Context, chainID string) bool { +func (k Keeper) HasChainInfo(ctx context.Context, chainID string) bool { store := k.chainInfoStore(ctx) return store.Has([]byte(chainID)) } @@ -48,7 +48,7 @@ func (k Keeper) HasChainInfo(ctx sdk.Context, chainID string) bool { // GetChainInfo returns the ChainInfo struct for a chain with a given ID // Since IBC does not provide API that allows to initialise chain info right before creating an IBC connection, // we can only check its existence every time, and return an empty one if it's not initialised yet. -func (k Keeper) GetChainInfo(ctx sdk.Context, chainID string) (*types.ChainInfo, error) { +func (k Keeper) GetChainInfo(ctx context.Context, chainID string) (*types.ChainInfo, error) { if !k.HasChainInfo(ctx, chainID) { return nil, types.ErrChainInfoNotFound } @@ -66,7 +66,7 @@ func (k Keeper) GetChainInfo(ctx sdk.Context, chainID string) (*types.ChainInfo, // Note that this function is triggered only upon receiving headers from the relayer, // and only a subset of headers in CZ are relayed. Thus TimestampedHeadersCount is not // equal to the total number of headers in CZ. -func (k Keeper) updateLatestHeader(ctx sdk.Context, chainID string, header *types.IndexedHeader) error { +func (k Keeper) updateLatestHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error { if header == nil { return errorsmod.Wrapf(types.ErrInvalidHeader, "header is nil") } @@ -87,7 +87,7 @@ func (k Keeper) updateLatestHeader(ctx sdk.Context, chainID string, header *type // - If there is a fork header at the same height, add this fork to the set of latest fork headers // - If this fork header is newer than the previous one, replace the old fork headers with this fork header // - If this fork header is older than the current latest fork, ignore -func (k Keeper) tryToUpdateLatestForkHeader(ctx sdk.Context, chainID string, header *types.IndexedHeader) error { +func (k Keeper) tryToUpdateLatestForkHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error { if header == nil { return errorsmod.Wrapf(types.ErrInvalidHeader, "header is nil") } @@ -118,7 +118,7 @@ func (k Keeper) tryToUpdateLatestForkHeader(ctx sdk.Context, chainID string, hea } // GetAllChainIDs gets all chain IDs that integrate Babylon -func (k Keeper) GetAllChainIDs(ctx sdk.Context) []string { +func (k Keeper) GetAllChainIDs(ctx context.Context) []string { chainIDs := []string{} iter := k.chainInfoStore(ctx).Iterator(nil, nil) defer iter.Close() @@ -135,7 +135,7 @@ func (k Keeper) GetAllChainIDs(ctx sdk.Context) []string { // prefix: ChainInfoKey // key: chainID // value: ChainInfo -func (k Keeper) chainInfoStore(ctx sdk.Context) prefix.Store { - store := ctx.KVStore(k.storeKey) - return prefix.NewStore(store, types.ChainInfoKey) +func (k Keeper) chainInfoStore(ctx context.Context) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + return prefix.NewStore(storeAdapter, types.ChainInfoKey) } diff --git a/x/zoneconcierge/keeper/epoch_chain_info_indexer.go b/x/zoneconcierge/keeper/epoch_chain_info_indexer.go index 286c36c1e..b5d1e9496 100644 --- a/x/zoneconcierge/keeper/epoch_chain_info_indexer.go +++ b/x/zoneconcierge/keeper/epoch_chain_info_indexer.go @@ -1,7 +1,9 @@ package keeper import ( - "github.com/cosmos/cosmos-sdk/store/prefix" + "context" + "cosmossdk.io/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" bbn "github.com/babylonchain/babylon/types" @@ -9,7 +11,7 @@ import ( ) // GetEpochChainInfo gets the latest chain info of a given epoch for a given chain ID -func (k Keeper) GetEpochChainInfo(ctx sdk.Context, chainID string, epochNumber uint64) (*types.ChainInfo, error) { +func (k Keeper) GetEpochChainInfo(ctx context.Context, chainID string, epochNumber uint64) (*types.ChainInfo, error) { if !k.EpochChainInfoExists(ctx, chainID, epochNumber) { return nil, types.ErrEpochChainInfoNotFound } @@ -23,14 +25,14 @@ func (k Keeper) GetEpochChainInfo(ctx sdk.Context, chainID string, epochNumber u } // EpochChainInfoExists checks if the latest chain info exists of a given epoch for a given chain ID -func (k Keeper) EpochChainInfoExists(ctx sdk.Context, chainID string, epochNumber uint64) bool { +func (k Keeper) EpochChainInfoExists(ctx context.Context, chainID string, epochNumber uint64) bool { store := k.epochChainInfoStore(ctx, chainID) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) return store.Has(epochNumberBytes) } // GetEpochHeaders gets the headers timestamped in a given epoch, in the ascending order -func (k Keeper) GetEpochHeaders(ctx sdk.Context, chainID string, epochNumber uint64) ([]*types.IndexedHeader, error) { +func (k Keeper) GetEpochHeaders(ctx context.Context, chainID string, epochNumber uint64) ([]*types.IndexedHeader, error) { headers := []*types.IndexedHeader{} // find the last timestamped header of this chain in the epoch @@ -70,11 +72,11 @@ func (k Keeper) GetEpochHeaders(ctx sdk.Context, chainID string, epochNumber uin // recordEpochChainInfo records the chain info for a given epoch number of given chain ID // where the latest chain info is retrieved from the chain info indexer -func (k Keeper) recordEpochChainInfo(ctx sdk.Context, chainID string, epochNumber uint64) { +func (k Keeper) recordEpochChainInfo(ctx context.Context, chainID string, epochNumber uint64) { // get the latest known chain info chainInfo, err := k.GetChainInfo(ctx, chainID) if err != nil { - k.Logger(ctx).Debug("chain info does not exist yet, nothing to record") + k.Logger(sdk.UnwrapSDKContext(ctx)).Debug("chain info does not exist yet, nothing to record") return } // NOTE: we can record epoch chain info without ancestor since IBC connection can be established at any height @@ -86,9 +88,9 @@ func (k Keeper) recordEpochChainInfo(ctx sdk.Context, chainID string, epochNumbe // prefix: EpochChainInfoKey || chainID // key: epochNumber // value: ChainInfo -func (k Keeper) epochChainInfoStore(ctx sdk.Context, chainID string) prefix.Store { - store := ctx.KVStore(k.storeKey) - epochChainInfoStore := prefix.NewStore(store, types.EpochChainInfoKey) +func (k Keeper) epochChainInfoStore(ctx context.Context, chainID string) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + epochChainInfoStore := prefix.NewStore(storeAdapter, types.EpochChainInfoKey) chainIDBytes := []byte(chainID) return prefix.NewStore(epochChainInfoStore, chainIDBytes) } diff --git a/x/zoneconcierge/keeper/epoch_chain_info_indexer_test.go b/x/zoneconcierge/keeper/epoch_chain_info_indexer_test.go index db7f71cfe..cf22dd09e 100644 --- a/x/zoneconcierge/keeper/epoch_chain_info_indexer_test.go +++ b/x/zoneconcierge/keeper/epoch_chain_info_indexer_test.go @@ -4,9 +4,11 @@ import ( "math/rand" "testing" - "github.com/babylonchain/babylon/testutil/datagen" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/testutil/datagen" ) func FuzzEpochChainInfoIndexer(f *testing.F) { @@ -15,29 +17,29 @@ func FuzzEpochChainInfoIndexer(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - epochingKeeper := babylonApp.EpochingKeeper + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" - ctx := babylonChain.GetContext() hooks := zcKeeper.Hooks() // enter a random epoch epochNum := datagen.RandomInt(r, 10) for j := uint64(0); j < epochNum; j++ { - epochingKeeper.IncEpoch(ctx) + babylonApp.EpochingKeeper.IncEpoch(ctx) } // invoke the hook a random number of times to simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 numForkHeaders := datagen.RandomInt(r, 10) + 1 - SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders, numForkHeaders) + SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, 0, numHeaders, numForkHeaders) // end this epoch hooks.AfterEpochEnds(ctx, epochNum) // check if the chain info of this epoch is recorded or not - chainInfo, err := zcKeeper.GetEpochChainInfo(ctx, czChain.ChainID, epochNum) + chainInfo, err := zcKeeper.GetEpochChainInfo(ctx, czChainID, epochNum) require.NoError(t, err) require.Equal(t, numHeaders-1, chainInfo.LatestHeader.Height) require.Equal(t, numHeaders, chainInfo.TimestampedHeadersCount) @@ -51,15 +53,14 @@ func FuzzGetEpochHeaders(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - epochingKeeper := babylonApp.EpochingKeeper + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" - ctx := babylonChain.GetContext() hooks := zcKeeper.Hooks() numReqs := datagen.RandomInt(r, 5) + 1 - epochNumList := []uint64{datagen.RandomInt(r, 10) + 1} nextHeightList := []uint64{0} numHeadersList := []uint64{} @@ -72,12 +73,12 @@ func FuzzGetEpochHeaders(f *testing.F) { epochNum := epochNumList[i] // enter a random epoch if i == 0 { - for j := uint64(0); j < epochNum; j++ { - epochingKeeper.IncEpoch(ctx) + for j := uint64(1); j < epochNum; j++ { // starting from epoch 1 + babylonApp.EpochingKeeper.IncEpoch(ctx) } } else { for j := uint64(0); j < epochNum-epochNumList[i-1]; j++ { - epochingKeeper.IncEpoch(ctx) + babylonApp.EpochingKeeper.IncEpoch(ctx) } } @@ -85,7 +86,7 @@ func FuzzGetEpochHeaders(f *testing.F) { numHeadersList = append(numHeadersList, datagen.RandomInt(r, 100)+1) numForkHeadersList = append(numForkHeadersList, datagen.RandomInt(r, 10)+1) // trigger hooks to append these headers and fork headers - expectedHeaders, _ := SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, nextHeightList[i], numHeadersList[i], numForkHeadersList[i]) + expectedHeaders, _ := SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, nextHeightList[i], numHeadersList[i], numForkHeadersList[i]) expectedHeadersMap[epochNum] = expectedHeaders // prepare nextHeight for the next request nextHeightList = append(nextHeightList, nextHeightList[i]+numHeadersList[i]) @@ -100,11 +101,11 @@ func FuzzGetEpochHeaders(f *testing.F) { for i := uint64(0); i < numReqs; i++ { epochNum := epochNumList[i] // check if the headers are same as expected - headers, err := zcKeeper.GetEpochHeaders(ctx, czChain.ChainID, epochNum) + headers, err := zcKeeper.GetEpochHeaders(ctx, czChainID, epochNum) require.NoError(t, err) require.Equal(t, len(expectedHeadersMap[epochNum]), len(headers)) for j := 0; j < len(expectedHeadersMap[epochNum]); j++ { - require.Equal(t, expectedHeadersMap[epochNum][j].Header.LastCommitHash, headers[j].Hash) + require.Equal(t, expectedHeadersMap[epochNum][j].Header.AppHash, headers[j].Hash) } } }) diff --git a/x/zoneconcierge/keeper/epochs.go b/x/zoneconcierge/keeper/epochs.go index ac0511a19..ad0a478d0 100644 --- a/x/zoneconcierge/keeper/epochs.go +++ b/x/zoneconcierge/keeper/epochs.go @@ -1,51 +1,70 @@ package keeper import ( - btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "context" + 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) { +// GetLastSentSegment get last broadcasted btc light client segment +func (k Keeper) GetLastSentSegment(ctx context.Context) *types.BTCChainSegment { + store := k.storeService.OpenKVStore(ctx) + has, err := store.Has(types.LastSentBTCSegmentKey) + if err != nil { + panic(err) + } + if !has { return nil } - btcTipBytes := store.Get(types.FinalizingBTCTipKey) - var btcTip btclctypes.BTCHeaderInfo - k.cdc.MustUnmarshal(btcTipBytes, &btcTip) - return &btcTip + segmentBytes, err := store.Get(types.LastSentBTCSegmentKey) + if err != nil { + panic(err) + } + var segment types.BTCChainSegment + k.cdc.MustUnmarshal(segmentBytes, &segment) + return &segment } -// setFinalizingBTCTip sets the last finalised BTC tip +// setLastSentSegment sets the last segment which was broadcasted to the other light clients // 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) +func (k Keeper) setLastSentSegment(ctx context.Context, segment *types.BTCChainSegment) { + store := k.storeService.OpenKVStore(ctx) + segmentBytes := k.cdc.MustMarshal(segment) + if err := store.Set(types.LastSentBTCSegmentKey, segmentBytes); err != nil { + panic(err) + } } // 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) { - store := ctx.KVStore(k.storeKey) - if !store.Has(types.FinalizedEpochKey) { +func (k Keeper) GetFinalizedEpoch(ctx context.Context) (uint64, error) { + store := k.storeService.OpenKVStore(ctx) + has, err := store.Has(types.FinalizedEpochKey) + if err != nil { + panic(err) + } + if !has { return 0, types.ErrFinalizedEpochNotFound } - epochNumberBytes := store.Get(types.FinalizedEpochKey) + epochNumberBytes, err := store.Get(types.FinalizedEpochKey) + if err != nil { + panic(err) + } return sdk.BigEndianToUint64(epochNumberBytes), nil } // setFinalizedEpoch sets the last finalised epoch // called upon each AfterRawCheckpointFinalized hook invocation -func (k Keeper) setFinalizedEpoch(ctx sdk.Context, epochNumber uint64) { - store := ctx.KVStore(k.storeKey) +func (k Keeper) setFinalizedEpoch(ctx context.Context, epochNumber uint64) { + store := k.storeService.OpenKVStore(ctx) epochNumberBytes := sdk.Uint64ToBigEndian(epochNumber) - store.Set(types.FinalizedEpochKey, epochNumberBytes) + if err := store.Set(types.FinalizedEpochKey, epochNumberBytes); err != nil { + panic(err) + } } -func (k Keeper) GetEpoch(ctx sdk.Context) *epochingtypes.Epoch { +func (k Keeper) GetEpoch(ctx context.Context) *epochingtypes.Epoch { return k.epochingKeeper.GetEpoch(ctx) } diff --git a/x/zoneconcierge/keeper/fork_indexer.go b/x/zoneconcierge/keeper/fork_indexer.go index c2ab37c9b..2ec7e6ba3 100644 --- a/x/zoneconcierge/keeper/fork_indexer.go +++ b/x/zoneconcierge/keeper/fork_indexer.go @@ -2,15 +2,17 @@ package keeper import ( "bytes" + "context" + "github.com/cosmos/cosmos-sdk/runtime" sdkerrors "cosmossdk.io/errors" + "cosmossdk.io/store/prefix" "github.com/babylonchain/babylon/x/zoneconcierge/types" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) // GetForks returns a list of forked headers at a given height -func (k Keeper) GetForks(ctx sdk.Context, chainID string, height uint64) *types.Forks { +func (k Keeper) GetForks(ctx context.Context, chainID string, height uint64) *types.Forks { store := k.forkStore(ctx, chainID) heightBytes := sdk.Uint64ToBigEndian(height) // if no fork at the moment, create an empty struct @@ -26,7 +28,7 @@ func (k Keeper) GetForks(ctx sdk.Context, chainID string, height uint64) *types. } // insertForkHeader inserts a forked header to the list of forked headers at the same height -func (k Keeper) insertForkHeader(ctx sdk.Context, chainID string, header *types.IndexedHeader) error { +func (k Keeper) insertForkHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error { if header == nil { return sdkerrors.Wrapf(types.ErrInvalidHeader, "header is nil") } @@ -48,9 +50,9 @@ func (k Keeper) insertForkHeader(ctx sdk.Context, chainID string, header *types. // prefix: ForkKey || chainID // key: height that this fork starts from // value: a list of IndexedHeader, representing each header in the fork -func (k Keeper) forkStore(ctx sdk.Context, chainID string) prefix.Store { - store := ctx.KVStore(k.storeKey) - forkStore := prefix.NewStore(store, types.ForkKey) +func (k Keeper) forkStore(ctx context.Context, chainID string) prefix.Store { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + forkStore := prefix.NewStore(storeAdapter, types.ForkKey) chainIDBytes := []byte(chainID) return prefix.NewStore(forkStore, chainIDBytes) } diff --git a/x/zoneconcierge/keeper/fork_indexer_test.go b/x/zoneconcierge/keeper/fork_indexer_test.go index 769df4db5..8195f6b01 100644 --- a/x/zoneconcierge/keeper/fork_indexer_test.go +++ b/x/zoneconcierge/keeper/fork_indexer_test.go @@ -4,6 +4,7 @@ import ( "math/rand" "testing" + "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/testutil/datagen" "github.com/stretchr/testify/require" ) @@ -14,34 +15,33 @@ func FuzzForkIndexer(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" // invoke the hook a random number of times to simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 numForkHeaders := datagen.RandomInt(r, 10) + 1 - _, forkHeaders := SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders, numForkHeaders) + _, forkHeaders := SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, 0, numHeaders, numForkHeaders) // check if the fork is updated or not - forks := zcKeeper.GetForks(ctx, czChain.ChainID, numHeaders-1) + forks := zcKeeper.GetForks(ctx, czChainID, numHeaders-1) require.Equal(t, numForkHeaders, uint64(len(forks.Headers))) for i := range forks.Headers { - require.Equal(t, czChain.ChainID, forks.Headers[i].ChainId) + require.Equal(t, czChainID, forks.Headers[i].ChainId) require.Equal(t, numHeaders-1, forks.Headers[i].Height) - require.Equal(t, forkHeaders[i].Header.LastCommitHash, forks.Headers[i].Hash) + require.Equal(t, forkHeaders[i].Header.AppHash, forks.Headers[i].Hash) } // check if the chain info is updated or not - chainInfo, err := zcKeeper.GetChainInfo(ctx, czChain.ChainID) + chainInfo, err := zcKeeper.GetChainInfo(ctx, czChainID) require.NoError(t, err) require.Equal(t, numForkHeaders, uint64(len(chainInfo.LatestForks.Headers))) for i := range forks.Headers { - require.Equal(t, czChain.ChainID, chainInfo.LatestForks.Headers[i].ChainId) + require.Equal(t, czChainID, chainInfo.LatestForks.Headers[i].ChainId) require.Equal(t, numHeaders-1, chainInfo.LatestForks.Headers[i].Height) - require.Equal(t, forkHeaders[i].Header.LastCommitHash, chainInfo.LatestForks.Headers[i].Hash) + require.Equal(t, forkHeaders[i].Header.AppHash, chainInfo.LatestForks.Headers[i].Hash) } }) } diff --git a/x/zoneconcierge/keeper/grpc_query_test.go b/x/zoneconcierge/keeper/grpc_query_test.go index 4a5139e0f..ba6dbf6e5 100644 --- a/x/zoneconcierge/keeper/grpc_query_test.go +++ b/x/zoneconcierge/keeper/grpc_query_test.go @@ -4,11 +4,10 @@ import ( "math/rand" "testing" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" - tmtypes "github.com/cometbft/cometbft/types" + "github.com/babylonchain/babylon/app" + btclightclienttypes "github.com/babylonchain/babylon/x/btclightclient/types" "github.com/cosmos/cosmos-sdk/types/query" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -32,11 +31,9 @@ func FuzzChainList(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, _, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) // invoke the hook a random number of times with random chain IDs numHeaders := datagen.RandomInt(r, 100) + 1 @@ -51,7 +48,7 @@ func FuzzChainList(f *testing.F) { allChainIDs = append(allChainIDs, chainID) } header := datagen.GenRandomIBCTMHeader(r, chainID, 0) - hooks.AfterHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) + zcKeeper.HandleHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) } limit := datagen.RandomInt(r, len(allChainIDs)) + 1 @@ -79,11 +76,9 @@ func FuzzChainsInfo(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, _, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) var ( chainsInfo []chainInfo @@ -94,7 +89,7 @@ func FuzzChainsInfo(f *testing.F) { chainID := datagen.GenRandomHexStr(r, 30) numHeaders := datagen.RandomInt(r, 100) + 1 numForkHeaders := datagen.RandomInt(r, 10) + 1 - SimulateHeadersAndForksViaHook(ctx, r, hooks, chainID, 0, numHeaders, numForkHeaders) + SimulateNewHeadersAndForks(ctx, r, &zcKeeper, chainID, 0, numHeaders, numForkHeaders) chainIDs = append(chainIDs, chainID) chainsInfo = append(chainsInfo, chainInfo{ @@ -123,31 +118,30 @@ func FuzzHeader(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" // invoke the hook a random number of times to simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 2 numForkHeaders := datagen.RandomInt(r, 10) + 1 - headers, forkHeaders := SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders, numForkHeaders) + headers, forkHeaders := SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, 0, numHeaders, numForkHeaders) // find header at a random height and assert correctness against the expected header randomHeight := datagen.RandomInt(r, int(numHeaders-1)) - resp, err := zcKeeper.Header(ctx, &zctypes.QueryHeaderRequest{ChainId: czChain.ChainID, Height: randomHeight}) + resp, err := zcKeeper.Header(ctx, &zctypes.QueryHeaderRequest{ChainId: czChainID, Height: randomHeight}) require.NoError(t, err) - require.Equal(t, headers[randomHeight].Header.LastCommitHash, resp.Header.Hash) + require.Equal(t, headers[randomHeight].Header.AppHash, resp.Header.Hash) require.Len(t, resp.ForkHeaders.Headers, 0) // find the last header and fork headers then assert correctness - resp, err = zcKeeper.Header(ctx, &zctypes.QueryHeaderRequest{ChainId: czChain.ChainID, Height: numHeaders - 1}) + resp, err = zcKeeper.Header(ctx, &zctypes.QueryHeaderRequest{ChainId: czChainID, Height: numHeaders - 1}) require.NoError(t, err) - require.Equal(t, headers[numHeaders-1].Header.LastCommitHash, resp.Header.Hash) + require.Equal(t, headers[numHeaders-1].Header.AppHash, resp.Header.Hash) require.Len(t, resp.ForkHeaders.Headers, int(numForkHeaders)) for i := 0; i < int(numForkHeaders); i++ { - require.Equal(t, forkHeaders[i].Header.LastCommitHash, resp.ForkHeaders.Headers[i].Hash) + require.Equal(t, forkHeaders[i].Header.AppHash, resp.ForkHeaders.Headers[i].Hash) } }) } @@ -157,10 +151,11 @@ func FuzzEpochChainsInfo(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, _, babylonApp := SetupTest(t) + + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper + ctx := babylonApp.NewContext(false) - ctx := babylonChain.GetContext() hooks := zcKeeper.Hooks() // generate a random number of chains @@ -191,7 +186,7 @@ func FuzzEpochChainsInfo(f *testing.F) { numForkHeaders := datagen.RandomInt(r, 10) + 1 // trigger hooks to append these headers and fork headers - SimulateHeadersAndForksViaHook(ctx, r, hooks, chainID, chainHeaderStartHeights[j], numHeaders, numForkHeaders) + SimulateNewHeadersAndForks(ctx, r, &zcKeeper, chainID, chainHeaderStartHeights[j], numHeaders, numForkHeaders) epochToChainInfo[epochNum][chainID] = chainInfo{ chainID: chainID, @@ -255,21 +250,20 @@ func FuzzListHeaders(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - hooks := zcKeeper.Hooks() + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" // invoke the hook a random number of times to simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 numForkHeaders := datagen.RandomInt(r, 10) + 1 - headers, _ := SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, 0, numHeaders, numForkHeaders) + headers, _ := SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, 0, numHeaders, numForkHeaders) // a request with randomised pagination limit := datagen.RandomInt(r, int(numHeaders)) + 1 req := &zctypes.QueryListHeadersRequest{ - ChainId: czChain.ChainID, + ChainId: czChainID, Pagination: &query.PageRequest{ Limit: limit, }, @@ -278,7 +272,7 @@ func FuzzListHeaders(f *testing.F) { require.NoError(t, err) require.Equal(t, int(limit), len(resp.Headers)) for i := uint64(0); i < limit; i++ { - require.Equal(t, headers[i].Header.LastCommitHash, resp.Headers[i].Hash) + require.Equal(t, headers[i].Header.AppHash, resp.Headers[i].Hash) } }) } @@ -289,11 +283,12 @@ func FuzzListEpochHeaders(f *testing.F) { f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) - _, babylonChain, czChain, babylonApp := SetupTest(t) + babylonApp := app.Setup(t, false) zcKeeper := babylonApp.ZoneConciergeKeeper epochingKeeper := babylonApp.EpochingKeeper + ctx := babylonApp.NewContext(false) + czChainID := "test-chainid" - ctx := babylonChain.GetContext() hooks := zcKeeper.Hooks() numReqs := datagen.RandomInt(r, 5) + 1 @@ -310,7 +305,7 @@ func FuzzListEpochHeaders(f *testing.F) { epochNum := epochNumList[i] // enter a random epoch if i == 0 { - for j := uint64(0); j < epochNum; j++ { + for j := uint64(1); j < epochNum; j++ { // starting from epoch 1 epochingKeeper.IncEpoch(ctx) } } else { @@ -323,7 +318,7 @@ func FuzzListEpochHeaders(f *testing.F) { numHeadersList = append(numHeadersList, datagen.RandomInt(r, 100)+1) numForkHeadersList = append(numForkHeadersList, datagen.RandomInt(r, 10)+1) // trigger hooks to append these headers and fork headers - expectedHeaders, _ := SimulateHeadersAndForksViaHook(ctx, r, hooks, czChain.ChainID, nextHeightList[i], numHeadersList[i], numForkHeadersList[i]) + expectedHeaders, _ := SimulateNewHeadersAndForks(ctx, r, &zcKeeper, czChainID, nextHeightList[i], numHeadersList[i], numForkHeadersList[i]) expectedHeadersMap[epochNum] = expectedHeaders // prepare nextHeight for the next request nextHeightList = append(nextHeightList, nextHeightList[i]+numHeadersList[i]) @@ -339,7 +334,7 @@ func FuzzListEpochHeaders(f *testing.F) { epochNum := epochNumList[i] // make request req := &zctypes.QueryListEpochHeadersRequest{ - ChainId: czChain.ChainID, + ChainId: czChainID, EpochNum: epochNum, } resp, err := zcKeeper.ListEpochHeaders(ctx, req) @@ -349,7 +344,7 @@ func FuzzListEpochHeaders(f *testing.F) { headers := resp.Headers require.Equal(t, len(expectedHeadersMap[epochNum]), len(headers)) for j := 0; j < len(expectedHeadersMap[epochNum]); j++ { - require.Equal(t, expectedHeadersMap[epochNum][j].Header.LastCommitHash, headers[j].Hash) + require.Equal(t, expectedHeadersMap[epochNum][j].Header.AppHash, headers[j].Hash) } } }) @@ -381,6 +376,7 @@ func FuzzFinalizedChainInfo(f *testing.F) { Ckpt: randomRawCkpt, }, nil, ).AnyTimes() + btccKeeper.EXPECT().GetParams(gomock.Any()).Return(btcctypes.DefaultParams()).AnyTimes() btccKeeper.EXPECT().GetBestSubmission(gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return( btcctypes.Finalized, &btcctypes.SubmissionKey{ @@ -394,21 +390,13 @@ func FuzzFinalizedChainInfo(f *testing.F) { epochingKeeper := zctypes.NewMockEpochingKeeper(ctrl) 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().GetMainChainFrom(gomock.Any(), gomock.Any()).Return([]*btclightclienttypes.BTCHeaderInfo{mockBTCHeaderInfo}).AnyTimes() btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(mockBTCHeaderInfo).AnyTimes() - // mock Tendermint client - // TODO: integration tests with Tendermint - tmClient := zctypes.NewMockTMClient(ctrl) - resTx := &tmrpctypes.ResultTx{ - Proof: tmtypes.TxProof{}, - } - tmClient.EXPECT().Tx(gomock.Any(), gomock.Any(), true).Return(resTx, nil).AnyTimes() - - zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, btclcKeeper, checkpointingKeeper, btccKeeper, epochingKeeper, tmClient) + zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, btclcKeeper, checkpointingKeeper, btccKeeper, epochingKeeper) hooks := zcKeeper.Hooks() var ( @@ -417,13 +405,13 @@ func FuzzFinalizedChainInfo(f *testing.F) { ) numChains := datagen.RandomInt(r, 100) + 1 for i := uint64(0); i < numChains; i++ { - czChainIDLen := datagen.RandomInt(r, 50) + 1 + czChainIDLen := datagen.RandomInt(r, 40) + 10 czChainID := string(datagen.GenRandomByteArray(r, czChainIDLen)) // invoke the hook a random number of times to simulate a random number of blocks numHeaders := datagen.RandomInt(r, 100) + 1 numForkHeaders := datagen.RandomInt(r, 10) + 1 - SimulateHeadersAndForksViaHook(ctx, r, hooks, czChainID, 0, numHeaders, numForkHeaders) + SimulateNewHeadersAndForks(ctx, r, zcKeeper, czChainID, 0, numHeaders, numForkHeaders) chainIDs = append(chainIDs, czChainID) chainsInfo = append(chainsInfo, chainInfo{ diff --git a/x/zoneconcierge/keeper/header_handler.go b/x/zoneconcierge/keeper/header_handler.go new file mode 100644 index 000000000..3bfdd046e --- /dev/null +++ b/x/zoneconcierge/keeper/header_handler.go @@ -0,0 +1,73 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonchain/babylon/x/zoneconcierge/types" +) + +// HandleHeaderWithValidCommit handles a CZ header with a valid QC +func (k Keeper) HandleHeaderWithValidCommit(ctx context.Context, txHash []byte, header *types.HeaderInfo, isOnFork bool) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + babylonHeader := sdkCtx.HeaderInfo() + indexedHeader := types.IndexedHeader{ + ChainId: header.ChainId, + Hash: header.AppHash, + Height: header.Height, + Time: &header.Time, + BabylonHeaderHash: babylonHeader.AppHash, + BabylonHeaderHeight: uint64(babylonHeader.Height), + BabylonEpoch: k.GetEpoch(ctx).EpochNumber, + BabylonTxHash: txHash, + } + + k.Logger(sdkCtx).Debug("found new IBC header", "header", indexedHeader) + + var ( + chainInfo *types.ChainInfo + err error + ) + if !k.HasChainInfo(ctx, indexedHeader.ChainId) { + // chain info does not exist yet, initialise chain info for this chain + chainInfo, err = k.InitChainInfo(ctx, indexedHeader.ChainId) + if err != nil { + panic(fmt.Errorf("failed to initialize chain info of %s: %w", indexedHeader.ChainId, err)) + } + } else { + // get chain info + chainInfo, err = k.GetChainInfo(ctx, indexedHeader.ChainId) + if err != nil { + panic(fmt.Errorf("failed to get chain info of %s: %w", indexedHeader.ChainId, err)) + } + } + + if isOnFork { + // insert header to fork index + if err := k.insertForkHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { + panic(err) + } + // update the latest fork in chain info + if err := k.tryToUpdateLatestForkHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { + panic(err) + } + } else { + // ensure the header is the latest one, otherwise ignore it + // NOTE: while an old header is considered acceptable in IBC-Go (see Case_valid_past_update), but + // ZoneConcierge should not checkpoint it since Babylon requires monotonic checkpointing + if !chainInfo.IsLatestHeader(&indexedHeader) { + return + } + + // insert header to canonical chain index + if err := k.insertHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { + panic(err) + } + // update the latest canonical header in chain info + if err := k.updateLatestHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { + panic(err) + } + } +} diff --git a/x/zoneconcierge/keeper/hooks.go b/x/zoneconcierge/keeper/hooks.go index 82d573955..f49d823c1 100644 --- a/x/zoneconcierge/keeper/hooks.go +++ b/x/zoneconcierge/keeper/hooks.go @@ -1,13 +1,12 @@ package keeper import ( - "fmt" + "context" sdk "github.com/cosmos/cosmos-sdk/types" checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - extendedkeeper "github.com/babylonchain/babylon/x/zoneconcierge/extended-client-keeper" "github.com/babylonchain/babylon/x/zoneconcierge/types" ) @@ -16,73 +15,13 @@ type Hooks struct { } // ensures Hooks implements ClientHooks interfaces -var _ extendedkeeper.ClientHooks = Hooks{} var _ checkpointingtypes.CheckpointingHooks = Hooks{} var _ epochingtypes.EpochingHooks = Hooks{} func (k Keeper) Hooks() Hooks { return Hooks{k} } -// AfterHeaderWithValidCommit is triggered upon each CZ header with a valid QC -func (h Hooks) AfterHeaderWithValidCommit(ctx sdk.Context, txHash []byte, header *extendedkeeper.HeaderInfo, isOnFork bool) { - babylonHeader := ctx.BlockHeader() - indexedHeader := types.IndexedHeader{ - ChainId: header.ChaindId, - Hash: header.Hash, - Height: header.Height, - Time: &header.Time, - BabylonHeader: &babylonHeader, - BabylonEpoch: h.k.GetEpoch(ctx).EpochNumber, - BabylonTxHash: txHash, - } - - var ( - chainInfo *types.ChainInfo - err error - ) - if !h.k.HasChainInfo(ctx, indexedHeader.ChainId) { - // chain info does not exist yet, initialise chain info for this chain - chainInfo, err = h.k.InitChainInfo(ctx, indexedHeader.ChainId) - if err != nil { - panic(fmt.Errorf("failed to initialize chain info of %s: %w", indexedHeader.ChainId, err)) - } - } else { - // get chain info - chainInfo, err = h.k.GetChainInfo(ctx, indexedHeader.ChainId) - if err != nil { - panic(fmt.Errorf("failed to get chain info of %s: %w", indexedHeader.ChainId, err)) - } - } - - if isOnFork { - // insert header to fork index - if err := h.k.insertForkHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { - panic(err) - } - // update the latest fork in chain info - if err := h.k.tryToUpdateLatestForkHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { - panic(err) - } - } else { - // ensure the header is the latest one, otherwise ignore it - // NOTE: while an old header is considered acceptable in IBC-Go (see Case_valid_past_update), but - // ZoneConcierge should not checkpoint it since Babylon requires monotonic checkpointing - if !chainInfo.IsLatestHeader(&indexedHeader) { - return - } - - // insert header to canonical chain index - if err := h.k.insertHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { - panic(err) - } - // update the latest canonical header in chain info - if err := h.k.updateLatestHeader(ctx, indexedHeader.ChainId, &indexedHeader); err != nil { - panic(err) - } - } -} - // AfterEpochEnds is triggered upon an epoch has ended -func (h Hooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) { +func (h Hooks) AfterEpochEnds(ctx context.Context, epoch uint64) { // upon an epoch has ended, index the current chain info for each CZ for _, chainID := range h.k.GetAllChainIDs(ctx) { h.k.recordEpochChainInfo(ctx, chainID, epoch) @@ -90,30 +29,32 @@ func (h Hooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) { } // AfterRawCheckpointFinalized is triggered upon an epoch has been finalised -func (h Hooks) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { +func (h Hooks) AfterRawCheckpointFinalized(ctx context.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) + headersToBroadcast := h.k.getHeadersToBroadcast(ctx) - // retrieve and update the last finalising BTC tip - btcTip := h.k.btclcKeeper.GetTipInfo(ctx) - h.k.setFinalizingBTCTip(ctx, btcTip) + // send BTC timestamp to all open channels with ZoneConcierge + h.k.BroadcastBTCTimestamps(ctx, epoch, headersToBroadcast) + // Update the last broadcasted segment + h.k.setLastSentSegment(ctx, &types.BTCChainSegment{ + BtcHeaders: headersToBroadcast, + }) return nil } // Other unused hooks -func (h Hooks) AfterBlsKeyRegistered(ctx sdk.Context, valAddr sdk.ValAddress) error { return nil } -func (h Hooks) AfterRawCheckpointConfirmed(ctx sdk.Context, epoch uint64) error { return nil } +func (h Hooks) AfterBlsKeyRegistered(ctx context.Context, valAddr sdk.ValAddress) error { return nil } +func (h Hooks) AfterRawCheckpointConfirmed(ctx context.Context, epoch uint64) error { return nil } -func (h Hooks) AfterRawCheckpointForgotten(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +func (h Hooks) AfterRawCheckpointForgotten(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return nil } -func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx sdk.Context, ckpt *checkpointingtypes.RawCheckpoint) error { +func (h Hooks) AfterRawCheckpointBlsSigVerified(ctx context.Context, ckpt *checkpointingtypes.RawCheckpoint) error { return nil } -func (h Hooks) AfterEpochBegins(ctx sdk.Context, epoch uint64) {} -func (h Hooks) BeforeSlashThreshold(ctx sdk.Context, valSet epochingtypes.ValidatorSet) {} +func (h Hooks) AfterEpochBegins(ctx context.Context, epoch uint64) {} +func (h Hooks) BeforeSlashThreshold(ctx context.Context, valSet epochingtypes.ValidatorSet) {} diff --git a/x/zoneconcierge/keeper/ibc_channels.go b/x/zoneconcierge/keeper/ibc_channels.go index a3709caee..d1ad90dc6 100644 --- a/x/zoneconcierge/keeper/ibc_channels.go +++ b/x/zoneconcierge/keeper/ibc_channels.go @@ -1,16 +1,17 @@ package keeper import ( + "context" sdk "github.com/cosmos/cosmos-sdk/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ) -func (k Keeper) GetAllChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { - return k.channelKeeper.GetAllChannels(ctx) +func (k Keeper) GetAllChannels(ctx context.Context) []channeltypes.IdentifiedChannel { + return k.channelKeeper.GetAllChannels(sdk.UnwrapSDKContext(ctx)) } // GetAllOpenZCChannels returns all open channels that are connected to ZoneConcierge's port -func (k Keeper) GetAllOpenZCChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { +func (k Keeper) GetAllOpenZCChannels(ctx context.Context) []channeltypes.IdentifiedChannel { zcPort := k.GetPort(ctx) channels := k.GetAllChannels(ctx) @@ -30,10 +31,11 @@ func (k Keeper) GetAllOpenZCChannels(ctx sdk.Context) []channeltypes.IdentifiedC // 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 { +func (k Keeper) isChannelUninitialized(ctx context.Context, channel channeltypes.IdentifiedChannel) bool { + sdkCtx := sdk.UnwrapSDKContext(ctx) 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) + nextSeqSend, _ := k.channelKeeper.GetNextSequenceSend(sdkCtx, portID, channelID) return nextSeqSend == 1 } diff --git a/x/zoneconcierge/keeper/ibc_header_decorator.go b/x/zoneconcierge/keeper/ibc_header_decorator.go new file mode 100644 index 000000000..1e8967be2 --- /dev/null +++ b/x/zoneconcierge/keeper/ibc_header_decorator.go @@ -0,0 +1,105 @@ +package keeper + +import ( + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" //nolint:staticcheck + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + + "github.com/babylonchain/babylon/x/zoneconcierge/types" +) + +var _ sdk.PostDecorator = &IBCHeaderDecorator{} + +type IBCHeaderDecorator struct { + k Keeper +} + +// NewIBCHeaderDecorator creates a new IBCHeaderDecorator +func NewIBCHeaderDecorator(k Keeper) *IBCHeaderDecorator { + return &IBCHeaderDecorator{ + k: k, + } +} + +func (d *IBCHeaderDecorator) getHeaderAndClientState(ctx sdk.Context, m sdk.Msg) (*types.HeaderInfo, *ibctmtypes.ClientState) { + // ensure the message is MsgUpdateClient + msgUpdateClient, ok := m.(*clienttypes.MsgUpdateClient) + if !ok { + return nil, nil + } + // unpack ClientMsg inside MsgUpdateClient + clientMsg, err := clienttypes.UnpackClientMessage(msgUpdateClient.ClientMessage) + if err != nil { + return nil, nil + } + // ensure the ClientMsg is a Comet header + ibctmHeader, ok := clientMsg.(*ibctmtypes.Header) + if !ok { + return nil, nil + } + + // all good, we get the headerInfo + headerInfo := &types.HeaderInfo{ + ClientId: msgUpdateClient.ClientId, + ChainId: ibctmHeader.Header.ChainID, + AppHash: ibctmHeader.Header.AppHash, + Height: uint64(ibctmHeader.Header.Height), + Time: ibctmHeader.Header.Time, + } + + // ensure the corresponding clientState exists + clientState, exist := d.k.clientKeeper.GetClientState(ctx, msgUpdateClient.ClientId) + if !exist { + return nil, nil + } + // ensure the clientState is a Comet clientState + cmtClientState, ok := clientState.(*ibctmtypes.ClientState) + if !ok { + return nil, nil + } + + return headerInfo, cmtClientState +} + +func (d *IBCHeaderDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate, success bool, next sdk.PostHandler) (sdk.Context, error) { + // only do this when finalizing a block or simulating the current tx + if ctx.ExecMode() != sdk.ExecModeFinalize && !simulate { + return next(ctx, tx, simulate, success) + } + // ignore unsuccessful tx + // NOTE: tx with a misbehaving header will still succeed, but will make the client to be frozen + if !success { + return next(ctx, tx, simulate, success) + } + + // calculate tx hash + txHash := tmhash.Sum(ctx.TxBytes()) + + for _, msg := range tx.GetMsgs() { + // try to extract the headerInfo and the client's status + headerInfo, clientState := d.getHeaderAndClientState(ctx, msg) + if headerInfo == nil { + continue + } + + // FrozenHeight is non-zero -> client is frozen -> this is a fork header + // NOTE: A valid tx can ONLY have a single fork header msg, and this fork + // header msg can ONLY be the LAST msg in this tx. If there is a fork + // header before a canonical header in a tx, then the client will be + // frozen upon the fork header, and the subsequent canonical header will + // fail, eventually failing the entire tx. All state updates due to this + // failed tx will be rolled back. + isOnFork := !clientState.FrozenHeight.IsZero() + d.k.HandleHeaderWithValidCommit(ctx, txHash, headerInfo, isOnFork) + + // unfreeze client (by setting FrozenHeight to zero again) if the client is frozen + // due to a fork header + if isOnFork { + clientState.FrozenHeight = clienttypes.ZeroHeight() + d.k.clientKeeper.SetClientState(ctx, headerInfo.ClientId, clientState) + } + } + + return next(ctx, tx, simulate, success) +} diff --git a/x/zoneconcierge/keeper/ibc_packet.go b/x/zoneconcierge/keeper/ibc_packet.go index 51b84cfec..266e8fd35 100644 --- a/x/zoneconcierge/keeper/ibc_packet.go +++ b/x/zoneconcierge/keeper/ibc_packet.go @@ -1,23 +1,26 @@ package keeper import ( + "context" "fmt" "time" + sdk "github.com/cosmos/cosmos-sdk/types" + errorsmod "cosmossdk.io/errors" - metrics "github.com/armon/go-metrics" "github.com/babylonchain/babylon/x/zoneconcierge/types" "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - coretypes "github.com/cosmos/ibc-go/v7/modules/core/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" //nolint:staticcheck + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + coretypes "github.com/cosmos/ibc-go/v8/modules/core/types" + "github.com/hashicorp/go-metrics" ) // SendIBCPacket sends an IBC packet to a channel // (adapted from https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/apps/transfer/keeper/relay.go) -func (k Keeper) SendIBCPacket(ctx sdk.Context, channel channeltypes.IdentifiedChannel, packetData *types.ZoneconciergePacketData) error { +func (k Keeper) SendIBCPacket(ctx context.Context, channel channeltypes.IdentifiedChannel, packetData *types.ZoneconciergePacketData) error { + sdkCtx := sdk.UnwrapSDKContext(ctx) // get src/dst ports and channels sourcePort := channel.PortId sourceChannel := channel.ChannelId @@ -26,18 +29,18 @@ func (k Keeper) SendIBCPacket(ctx sdk.Context, channel channeltypes.IdentifiedCh // begin createOutgoingPacket logic // See spec for this logic: https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay - channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel)) + channelCap, ok := k.scopedKeeper.GetCapability(sdkCtx, host.ChannelCapabilityPath(sourcePort, sourceChannel)) if !ok { return errorsmod.Wrapf(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability: sourcePort: %s, sourceChannel: %s", sourcePort, sourceChannel) } // timeout - timeoutPeriod := time.Duration(k.GetParams(ctx).IbcPacketTimeoutSeconds) * time.Second - timeoutTime := uint64(ctx.BlockHeader().Time.Add(timeoutPeriod).UnixNano()) + timeoutPeriod := time.Duration(k.GetParams(sdkCtx).IbcPacketTimeoutSeconds) * time.Second + timeoutTime := uint64(sdkCtx.HeaderInfo().Time.Add(timeoutPeriod).UnixNano()) zeroheight := clienttypes.ZeroHeight() seq, err := k.ics4Wrapper.SendPacket( - ctx, + sdkCtx, channelCap, sourcePort, sourceChannel, @@ -49,9 +52,9 @@ func (k Keeper) SendIBCPacket(ctx sdk.Context, channel channeltypes.IdentifiedCh // send packet if err != nil { // Failed/timeout packet should not make the system crash - k.Logger(ctx).Error(fmt.Sprintf("failed to send IBC packet (sequence number: %d) to channel %v port %s: %v", seq, destinationChannel, destinationPort, err)) + k.Logger(sdkCtx).Error(fmt.Sprintf("failed to send IBC packet (sequence number: %d) to channel %v port %s: %v", seq, destinationChannel, destinationPort, err)) } else { - k.Logger(ctx).Info(fmt.Sprintf("successfully sent IBC packet (sequence number: %d) to channel %v port %s", seq, destinationChannel, destinationPort)) + k.Logger(sdkCtx).Info(fmt.Sprintf("successfully sent IBC packet (sequence number: %d) to channel %v port %s", seq, destinationChannel, destinationPort)) } // metrics stuff diff --git a/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go index 1b44e9229..219025023 100644 --- a/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go +++ b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go @@ -1,17 +1,19 @@ package keeper import ( + "context" "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + 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 @@ -26,40 +28,27 @@ type finalizedInfo struct { } // getChainID gets the ID of the counterparty chain under the given channel -func (k Keeper) getChainID(ctx sdk.Context, channel channeltypes.IdentifiedChannel) (string, error) { +func (k Keeper) getChainID(ctx context.Context, channel channeltypes.IdentifiedChannel) (string, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) // get clientState under this channel - _, clientState, err := k.channelKeeper.GetChannelClientState(ctx, channel.PortId, channel.ChannelId) + _, clientState, err := k.channelKeeper.GetChannelClientState(sdkCtx, channel.PortId, channel.ChannelId) if err != nil { return "", err } - // cast clientState to tendermint clientState + // cast clientState to comet clientState // TODO: support for chains other than Cosmos zones - tmClient, ok := clientState.(*ibctmtypes.ClientState) + cmtClientState, ok := clientState.(*ibctmtypes.ClientState) if !ok { - return "", fmt.Errorf("client must be a Tendermint client, expected: %T, got: %T", &ibctmtypes.ClientState{}, tmClient) + return "", fmt.Errorf("client must be a Comet client, expected: %T, got: %T", &ibctmtypes.ClientState{}, cmtClientState) } - 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 + return cmtClientState.ChainId, nil } // 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) { +func (k Keeper) getFinalizedInfo( + ctx context.Context, + epochNum uint64, + headersToBroadcast []*btclctypes.BTCHeaderInfo) (*finalizedInfo, error) { finalizedEpochInfo, err := k.epochingKeeper.GetHistoricalEpoch(ctx, epochNum) if err != nil { return nil, err @@ -92,9 +81,6 @@ func (k Keeper) getFinalizedInfo(ctx sdk.Context, epochNum uint64) (*finalizedIn 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, @@ -102,7 +88,7 @@ func (k Keeper) getFinalizedInfo(ctx sdk.Context, epochNum uint64) (*finalizedIn BTCSubmissionKey: btcSubmissionKey, ProofEpochSealed: proofEpochSealed, ProofEpochSubmitted: proofEpochSubmitted, - BTCHeaders: btcHeaders, + BTCHeaders: headersToBroadcast, } return finalizedInfo, nil @@ -110,7 +96,12 @@ func (k Keeper) getFinalizedInfo(ctx sdk.Context, epochNum uint64) (*finalizedIn // 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) { +func (k Keeper) createBTCTimestamp( + ctx context.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 @@ -145,57 +136,103 @@ func (k Keeper) createBTCTimestamp(ctx sdk.Context, chainID string, channel chan RawCheckpoint: finalizedInfo.RawCheckpoint, BtcSubmissionKey: finalizedInfo.BTCSubmissionKey, Proof: &types.ProofFinalizedChainInfo{ - ProofTxInBlock: nil, - ProofHeaderInEpoch: nil, - ProofEpochSealed: finalizedInfo.ProofEpochSealed, - ProofEpochSubmitted: finalizedInfo.ProofEpochSubmitted, + ProofCzHeaderInEpoch: 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) + // get proofCZHeaderInEpoch + proofCZHeaderInEpoch, err := k.ProveCZHeaderInEpoch(ctx, finalizedChainInfo.LatestHeader, finalizedInfo.EpochInfo) 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) + return nil, fmt.Errorf("failed to generate proofCZHeaderInEpoch for chain %s: %w", chainID, err) } btcTimestamp.Header = finalizedChainInfo.LatestHeader - btcTimestamp.Proof.ProofTxInBlock = proofTxInBlock - btcTimestamp.Proof.ProofHeaderInEpoch = proofHeaderInEpoch + btcTimestamp.Proof.ProofCzHeaderInEpoch = proofCZHeaderInEpoch } return btcTimestamp, nil } +// getDeepEnoughBTCHeaders returns the last w+1 BTC headers, in which the 1st BTC header +// must be in the canonical chain assuming w-long reorg will never happen +// This function will only be triggered upon a finalised epoch, where w-deep BTC checkpoint +// is guaranteed. Thus the function is safe to be called upon generating BTC timestamps +func (k Keeper) getDeepEnoughBTCHeaders(ctx context.Context) []*btclctypes.BTCHeaderInfo { + wValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + startHeight := k.btclcKeeper.GetTipInfo(ctx).Height - wValue + return k.btclcKeeper.GetMainChainFrom(ctx, startHeight) +} + +// getHeadersToBroadcast retrieves headers to be broadcasted to all open IBC channels to ZoneConcierge +// The header to be broadcasted are: +// - either the whole known chain if we did not broadcast any headers yet +// - headers from the child of the most recent header we sent which is still in the main chain up to the current tip +func (k Keeper) getHeadersToBroadcast(ctx context.Context) []*btclctypes.BTCHeaderInfo { + + lastSegment := k.GetLastSentSegment(ctx) + + if lastSegment == nil { + // we did not send any headers yet, so we need to send the last w+1 BTC headers + // where w+1 is imposed by Babylon contract. This ensures that the first BTC header + // in Babylon contract will be w-deep + return k.getDeepEnoughBTCHeaders(ctx) + } + + // we already sent some headers, so we need to send headers from the child of the most recent header we sent + // which is still in the main chain. + // In most cases it will be header just after the tip, but in case of the forks it may as well be some older header + // of the segment + var initHeader *btclctypes.BTCHeaderInfo + for i := len(lastSegment.BtcHeaders) - 1; i >= 0; i-- { + header := lastSegment.BtcHeaders[i] + if k.btclcKeeper.GetHeaderByHash(ctx, header.Hash) != nil { + initHeader = header + break + } + } + + if initHeader == nil { + // if initHeader is nil, then this means a reorg happens such that all headers + // in the last segment are reverted. In this case, send the last w+1 BTC headers + return k.getDeepEnoughBTCHeaders(ctx) + } + + headersToSend := k.btclcKeeper.GetMainChainFrom(ctx, initHeader.Height+1) + + return headersToSend +} + // BroadcastBTCTimestamps sends an IBC packet of BTC timestamp to all open IBC channels to ZoneConcierge -func (k Keeper) BroadcastBTCTimestamps(ctx sdk.Context, epochNum uint64) { +func (k Keeper) BroadcastBTCTimestamps( + ctx context.Context, + epochNum uint64, + headersToBroadcast []*btclctypes.BTCHeaderInfo, +) { + sdkCtx := sdk.UnwrapSDKContext(ctx) // 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") + k.Logger(sdkCtx).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") + k.Logger(sdkCtx).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)) + k.Logger(sdkCtx).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) + finalizedInfo, err := k.getFinalizedInfo(ctx, epochNum, headersToBroadcast) 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) + k.Logger(sdkCtx).Error("failed to generate metadata shared across BTC timestamps in the same epoch, skip broadcasting BTC timestamps", "error", err) return } @@ -204,14 +241,14 @@ func (k Keeper) BroadcastBTCTimestamps(ctx sdk.Context, epochNum uint64) { // 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) + k.Logger(sdkCtx).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) + k.Logger(sdkCtx).Error("failed to generate BTC timestamp, skip sending BTC timestamp for this chain", "chainID", chainID, "error", err) continue } @@ -219,10 +256,8 @@ func (k Keeper) BroadcastBTCTimestamps(ctx sdk.Context, epochNum uint64) { 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) + k.Logger(sdkCtx).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/ibc_packet_btc_timestamp_test.go b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go new file mode 100644 index 000000000..531657d26 --- /dev/null +++ b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp_test.go @@ -0,0 +1,146 @@ +package keeper_test + +import ( + "context" + "math/rand" + "testing" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/testutil/datagen" + btclckeeper "github.com/babylonchain/babylon/x/btclightclient/keeper" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/stretchr/testify/require" +) + +func allFieldsEqual(a *btclctypes.BTCHeaderInfo, b *btclctypes.BTCHeaderInfo) bool { + return a.Height == b.Height && a.Hash.Eq(b.Hash) && a.Header.Eq(b.Header) && a.Work.Equal(*b.Work) +} + +// this function must not be used at difficulty adjustment boundaries, as then +// difficulty adjustment calculation will fail +func genRandomChain( + t *testing.T, + r *rand.Rand, + k *btclckeeper.Keeper, + ctx context.Context, + initialHeight uint64, + chainLength uint64, +) *datagen.BTCHeaderPartialChain { + initHeader := k.GetHeaderByHeight(ctx, initialHeight) + randomChain := datagen.NewBTCHeaderChainFromParentInfo( + r, + initHeader, + uint32(chainLength), + ) + err := k.InsertHeaders(ctx, randomChain.ChainToBytes()) + require.NoError(t, err) + tip := k.GetTipInfo(ctx) + randomChainTipInfo := randomChain.GetTipInfo() + require.True(t, allFieldsEqual(tip, randomChainTipInfo)) + return randomChain +} + +func FuzzGetHeadersToBroadcast(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + babylonApp := app.Setup(t, false) + zcKeeper := babylonApp.ZoneConciergeKeeper + btclcKeeper := babylonApp.BTCLightClientKeeper + ctx := babylonApp.NewContext(false) + + hooks := zcKeeper.Hooks() + + // insert a random number of BTC headers to BTC light client + wValue := babylonApp.BtcCheckpointKeeper.GetParams(ctx).CheckpointFinalizationTimeout + chainLength := datagen.RandomInt(r, 10) + wValue + genRandomChain( + t, + r, + &btclcKeeper, + ctx, + 0, + chainLength, + ) + + // finalise a random epoch + epochNum := datagen.RandomInt(r, 10) + err := hooks.AfterRawCheckpointFinalized(ctx, epochNum) + require.NoError(t, err) + // current tip + btcTip := btclcKeeper.GetTipInfo(ctx) + // assert the last segment is the last w+1 BTC headers + lastSegment := zcKeeper.GetLastSentSegment(ctx) + require.Len(t, lastSegment.BtcHeaders, int(wValue)+1) + for i := range lastSegment.BtcHeaders { + require.Equal(t, btclcKeeper.GetHeaderByHeight(ctx, btcTip.Height-wValue+uint64(i)), lastSegment.BtcHeaders[i]) + } + + // finalise another epoch, during which a small number of new BTC headers are inserted + epochNum += 1 + chainLength = datagen.RandomInt(r, 10) + 1 + genRandomChain( + t, + r, + &btclcKeeper, + ctx, + btcTip.Height, + chainLength, + ) + err = hooks.AfterRawCheckpointFinalized(ctx, epochNum) + require.NoError(t, err) + // assert the last segment is since the header after the last tip + lastSegment = zcKeeper.GetLastSentSegment(ctx) + require.Len(t, lastSegment.BtcHeaders, int(chainLength)) + for i := range lastSegment.BtcHeaders { + require.Equal(t, btclcKeeper.GetHeaderByHeight(ctx, uint64(i)+btcTip.Height+1), lastSegment.BtcHeaders[i]) + } + + // remember the current tip and the segment length + btcTip = btclcKeeper.GetTipInfo(ctx) + lastSegmentLength := uint64(len(lastSegment.BtcHeaders)) + + // finalise another epoch, during which a number of new BTC headers with reorg are inserted + epochNum += 1 + // reorg at a super random point + // NOTE: it's possible that the last segment is totally reverted. We want to be resilient against + // this, by sending the BTC headers since the last reorg point + reorgPoint := datagen.RandomInt(r, int(btcTip.Height)) + revertedChainLength := btcTip.Height - reorgPoint + // the fork chain needs to be longer than the canonical one + forkChainLength := revertedChainLength + datagen.RandomInt(r, 10) + 1 + genRandomChain( + t, + r, + &btclcKeeper, + ctx, + reorgPoint, + forkChainLength, + ) + err = hooks.AfterRawCheckpointFinalized(ctx, epochNum) + require.NoError(t, err) + // current tip + btcTip = btclcKeeper.GetTipInfo(ctx) + // assert the last segment is the last w+1 BTC headers + lastSegment = zcKeeper.GetLastSentSegment(ctx) + if revertedChainLength >= lastSegmentLength { + // the entire last segment is reverted, the last w+1 BTC headers should be sent + require.Len(t, lastSegment.BtcHeaders, int(wValue)+1) + // assert the consistency of w+1 sent BTC headers + for i := range lastSegment.BtcHeaders { + expectedHeight := btcTip.Height - wValue + uint64(i) + require.Equal(t, btclcKeeper.GetHeaderByHeight(ctx, expectedHeight), lastSegment.BtcHeaders[i]) + } + } else { + // only a subset headers of last segment are reverted, only the new fork should be sent + require.Len(t, lastSegment.BtcHeaders, int(forkChainLength)) + // assert the consistency of the sent fork BTC headers + for i := range lastSegment.BtcHeaders { + expectedHeight := btcTip.Height - forkChainLength + 1 + uint64(i) + require.Equal(t, btclcKeeper.GetHeaderByHeight(ctx, expectedHeight), lastSegment.BtcHeaders[i]) + } + } + }) +} diff --git a/x/zoneconcierge/keeper/keeper.go b/x/zoneconcierge/keeper/keeper.go index 8231d6a10..b34290f6e 100644 --- a/x/zoneconcierge/keeper/keeper.go +++ b/x/zoneconcierge/keeper/keeper.go @@ -1,23 +1,26 @@ package keeper import ( + "context" + + corestoretypes "cosmossdk.io/core/store" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" "github.com/babylonchain/babylon/x/zoneconcierge/types" - "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ) type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey + cdc codec.BinaryCodec + storeService corestoretypes.KVStoreService ics4Wrapper types.ICS4Wrapper + clientKeeper types.ClientKeeper channelKeeper types.ChannelKeeper portKeeper types.PortKeeper authKeeper types.AccountKeeper @@ -26,8 +29,7 @@ type ( checkpointingKeeper types.CheckpointingKeeper btccKeeper types.BtcCheckpointKeeper epochingKeeper types.EpochingKeeper - tmClient types.TMClient - storeQuerier sdk.Queryable + storeQuerier storetypes.Queryable scopedKeeper types.ScopedKeeper // the address capable of executing a MsgUpdateParams message. Typically, this // should be the x/gov module account. @@ -37,9 +39,9 @@ type ( func NewKeeper( cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, + storeService corestoretypes.KVStoreService, ics4Wrapper types.ICS4Wrapper, + clientKeeper types.ClientKeeper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, authKeeper types.AccountKeeper, @@ -48,16 +50,15 @@ func NewKeeper( checkpointingKeeper types.CheckpointingKeeper, btccKeeper types.BtcCheckpointKeeper, epochingKeeper types.EpochingKeeper, - tmClient types.TMClient, - storeQuerier sdk.Queryable, + storeQuerier storetypes.Queryable, scopedKeeper types.ScopedKeeper, authority string, ) *Keeper { return &Keeper{ cdc: cdc, - storeKey: storeKey, - memKey: memKey, + storeService: storeService, ics4Wrapper: ics4Wrapper, + clientKeeper: clientKeeper, channelKeeper: channelKeeper, portKeeper: portKeeper, authKeeper: authKeeper, @@ -66,7 +67,6 @@ func NewKeeper( checkpointingKeeper: checkpointingKeeper, btccKeeper: btccKeeper, epochingKeeper: epochingKeeper, - tmClient: tmClient, storeQuerier: storeQuerier, scopedKeeper: scopedKeeper, authority: authority, @@ -92,15 +92,21 @@ func (k Keeper) BindPort(ctx sdk.Context, portID string) error { } // GetPort returns the portID for the transfer module. Used in ExportGenesis -func (k Keeper) GetPort(ctx sdk.Context) string { - store := ctx.KVStore(k.storeKey) - return string(store.Get(types.PortKey)) +func (k Keeper) GetPort(ctx context.Context) string { + store := k.storeService.OpenKVStore(ctx) + port, err := store.Get(types.PortKey) + if err != nil { + panic(err) + } + return string(port) } // SetPort sets the portID for the transfer module. Used in InitGenesis -func (k Keeper) SetPort(ctx sdk.Context, portID string) { - store := ctx.KVStore(k.storeKey) - store.Set(types.PortKey, []byte(portID)) +func (k Keeper) SetPort(ctx context.Context, portID string) { + store := k.storeService.OpenKVStore(ctx) + if err := store.Set(types.PortKey, []byte(portID)); err != nil { + panic(err) + } } // AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function diff --git a/x/zoneconcierge/keeper/keeper_test.go b/x/zoneconcierge/keeper/keeper_test.go index eb1b63dc1..51b04f56d 100644 --- a/x/zoneconcierge/keeper/keeper_test.go +++ b/x/zoneconcierge/keeper/keeper_test.go @@ -1,59 +1,34 @@ package keeper_test import ( - "encoding/json" + "context" "math/rand" - "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - "github.com/babylonchain/babylon/app" "github.com/babylonchain/babylon/testutil/datagen" zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" ) -// SetupTest creates a coordinator with 2 test chains, and a ZoneConcierge keeper. -func SetupTest(t *testing.T) (*ibctesting.Coordinator, *ibctesting.TestChain, *ibctesting.TestChain, *app.BabylonApp) { - var bbnApp *app.BabylonApp - coordinator := ibctesting.NewCoordinator(t, 2) - // replace the first test chain with a Babylon chain - ibctesting.DefaultTestingAppInit = func() (ibctesting.TestingApp, map[string]json.RawMessage) { - babylonApp := app.Setup(t, false) - bbnApp = babylonApp - encCdc := app.GetEncodingConfig() - genesis := app.NewDefaultGenesisState(encCdc.Marshaler) - return babylonApp, genesis - } - babylonChainID := ibctesting.GetChainID(1) - coordinator.Chains[babylonChainID] = ibctesting.NewTestChain(t, coordinator, babylonChainID) - - babylonChain := coordinator.GetChain(ibctesting.GetChainID(1)) - czChain := coordinator.GetChain(ibctesting.GetChainID(2)) - - return coordinator, babylonChain, czChain, bbnApp -} - -// SimulateHeadersViaHook generates a non-zero number of canonical headers via the hook -func SimulateHeadersViaHook(ctx sdk.Context, r *rand.Rand, hooks zckeeper.Hooks, chainID string, startHeight uint64, numHeaders uint64) []*ibctmtypes.Header { +// SimulateNewHeaders generates a non-zero number of canonical headers +func SimulateNewHeaders(ctx context.Context, r *rand.Rand, k *zckeeper.Keeper, chainID string, startHeight uint64, numHeaders uint64) []*ibctmtypes.Header { headers := []*ibctmtypes.Header{} // invoke the hook a number of times to simulate a number of blocks for i := uint64(0); i < numHeaders; i++ { header := datagen.GenRandomIBCTMHeader(r, chainID, startHeight+i) - hooks.AfterHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) + k.HandleHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) headers = append(headers, header) } return headers } -// SimulateHeadersViaHook generates a random non-zero number of canonical headers and fork headers via the hook -func SimulateHeadersAndForksViaHook(ctx sdk.Context, r *rand.Rand, hooks zckeeper.Hooks, chainID string, startHeight uint64, numHeaders uint64, numForkHeaders uint64) ([]*ibctmtypes.Header, []*ibctmtypes.Header) { +// SimulateNewHeadersAndForks generates a random non-zero number of canonical headers and fork headers +func SimulateNewHeadersAndForks(ctx context.Context, r *rand.Rand, k *zckeeper.Keeper, chainID string, startHeight uint64, numHeaders uint64, numForkHeaders uint64) ([]*ibctmtypes.Header, []*ibctmtypes.Header) { headers := []*ibctmtypes.Header{} // invoke the hook a number of times to simulate a number of blocks for i := uint64(0); i < numHeaders; i++ { header := datagen.GenRandomIBCTMHeader(r, chainID, startHeight+i) - hooks.AfterHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) + k.HandleHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), false) headers = append(headers, header) } @@ -61,7 +36,7 @@ func SimulateHeadersAndForksViaHook(ctx sdk.Context, r *rand.Rand, hooks zckeepe forkHeaders := []*ibctmtypes.Header{} for i := uint64(0); i < numForkHeaders; i++ { header := datagen.GenRandomIBCTMHeader(r, chainID, startHeight+numHeaders-1) - hooks.AfterHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), true) + k.HandleHeaderWithValidCommit(ctx, datagen.GenRandomByteArray(r, 32), datagen.HeaderToHeaderInfo(header), true) forkHeaders = append(forkHeaders, header) } return headers, forkHeaders diff --git a/x/zoneconcierge/keeper/msg_server.go b/x/zoneconcierge/keeper/msg_server.go index 42d7a88fa..dee48dd04 100644 --- a/x/zoneconcierge/keeper/msg_server.go +++ b/x/zoneconcierge/keeper/msg_server.go @@ -21,14 +21,14 @@ 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. +// UpdateParams updates the params 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) } + if err := req.Params.Validate(); err != nil { + return nil, govtypes.ErrInvalidProposalMsg.Wrapf("invalid parameter: %v", err) + } ctx := sdk.UnwrapSDKContext(goCtx) if err := ms.SetParams(ctx, req.Params); err != nil { diff --git a/x/zoneconcierge/keeper/msg_server_test.go b/x/zoneconcierge/keeper/msg_server_test.go index d7e4e1502..942926490 100644 --- a/x/zoneconcierge/keeper/msg_server_test.go +++ b/x/zoneconcierge/keeper/msg_server_test.go @@ -1,17 +1 @@ package keeper_test - -import ( - "context" - "testing" - - keepertest "github.com/babylonchain/babylon/testutil/keeper" - "github.com/babylonchain/babylon/x/zoneconcierge/keeper" - "github.com/babylonchain/babylon/x/zoneconcierge/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -//nolint:unused -func setupMsgServer(t testing.TB) (types.MsgServer, context.Context) { - k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) - return keeper.NewMsgServerImpl(*k), sdk.WrapSDKContext(ctx) -} diff --git a/x/zoneconcierge/keeper/params.go b/x/zoneconcierge/keeper/params.go index 2cfae5c0f..04989eea9 100644 --- a/x/zoneconcierge/keeper/params.go +++ b/x/zoneconcierge/keeper/params.go @@ -1,28 +1,32 @@ package keeper import ( + "context" "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 { +func (k Keeper) SetParams(ctx context.Context, p types.Params) error { if err := p.Validate(); err != nil { return err } - store := ctx.KVStore(k.storeKey) + store := k.storeService.OpenKVStore(ctx) bz := k.cdc.MustMarshal(&p) - store.Set(types.ParamsKey, bz) + if err := store.Set(types.ParamsKey, bz); err != nil { + panic(err) + } 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) +func (k Keeper) GetParams(ctx context.Context) (p types.Params) { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.ParamsKey) + if err != nil { + panic(err) + } if bz == nil { return p } diff --git a/x/zoneconcierge/keeper/params_test.go b/x/zoneconcierge/keeper/params_test.go index 70c1f96a6..b8f8f772d 100644 --- a/x/zoneconcierge/keeper/params_test.go +++ b/x/zoneconcierge/keeper/params_test.go @@ -9,7 +9,7 @@ import ( ) func TestGetParams(t *testing.T) { - k, ctx := testkeeper.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) + k, ctx := testkeeper.ZoneConciergeKeeper(t, nil, nil, nil, nil) params := types.DefaultParams() if err := k.SetParams(ctx, params); err != nil { diff --git a/x/zoneconcierge/keeper/proof_block_in_epoch.go b/x/zoneconcierge/keeper/proof_block_in_epoch.go deleted file mode 100644 index 0cdbabd35..000000000 --- a/x/zoneconcierge/keeper/proof_block_in_epoch.go +++ /dev/null @@ -1,17 +0,0 @@ -package keeper - -import ( - epochingkeeper "github.com/babylonchain/babylon/x/epoching/keeper" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (k Keeper) ProveHeaderInEpoch(ctx sdk.Context, header *tmproto.Header, epoch *epochingtypes.Epoch) (*tmcrypto.Proof, error) { - return k.epochingKeeper.ProveAppHashInEpoch(ctx, uint64(header.Height), epoch.EpochNumber) -} - -func VerifyHeaderInEpoch(header *tmproto.Header, epoch *epochingtypes.Epoch, proof *tmcrypto.Proof) error { - return epochingkeeper.VerifyAppHashInclusion(header.AppHash, epoch.AppHashRoot, proof) -} diff --git a/x/zoneconcierge/keeper/proof_btc_timestamp.go b/x/zoneconcierge/keeper/proof_btc_timestamp.go new file mode 100644 index 000000000..8f6b52368 --- /dev/null +++ b/x/zoneconcierge/keeper/proof_btc_timestamp.go @@ -0,0 +1,129 @@ +package keeper + +import ( + "context" + "fmt" + + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + "github.com/babylonchain/babylon/x/zoneconcierge/types" +) + +func (k Keeper) ProveCZHeaderInEpoch(_ context.Context, header *types.IndexedHeader, epoch *epochingtypes.Epoch) (*cmtcrypto.ProofOps, error) { + czHeaderKey := types.GetCZHeaderKey(header.ChainId, header.Height) + _, _, proof, err := k.QueryStore(types.StoreKey, czHeaderKey, int64(epoch.GetSealerBlockHeight())) + if err != nil { + return nil, err + } + + return proof, nil +} + +func (k Keeper) ProveEpochInfo(epoch *epochingtypes.Epoch) (*cmtcrypto.ProofOps, error) { + epochInfoKey := types.GetEpochInfoKey(epoch.EpochNumber) + _, _, proof, err := k.QueryStore(epochingtypes.StoreKey, epochInfoKey, int64(epoch.GetSealerBlockHeight())) + if err != nil { + return nil, err + } + + return proof, nil +} + +func (k Keeper) ProveValSet(epoch *epochingtypes.Epoch) (*cmtcrypto.ProofOps, error) { + valSetKey := types.GetValSetKey(epoch.EpochNumber) + _, _, proof, err := k.QueryStore(checkpointingtypes.StoreKey, valSetKey, int64(epoch.GetSealerBlockHeight())) + if err != nil { + return nil, err + } + return proof, nil +} + +// ProveEpochSealed proves an epoch has been sealed, i.e., +// - the epoch's validator set has a valid multisig over the sealer header +// - the epoch's validator set is committed to the sealer header's app_hash +// - the epoch's metadata is committed to the sealer header's app_hash +func (k Keeper) ProveEpochSealed(ctx context.Context, epochNumber uint64) (*types.ProofEpochSealed, error) { + var ( + proof = &types.ProofEpochSealed{} + err error + ) + + // get the validator set of the sealed epoch + proof.ValidatorSet, err = k.checkpointingKeeper.GetBLSPubKeySet(ctx, epochNumber) + if err != nil { + return nil, err + } + + // get sealer header and the query height + epoch, err := k.epochingKeeper.GetHistoricalEpoch(ctx, epochNumber) + if err != nil { + return nil, err + } + + // proof of inclusion for epoch metadata in sealer header + proof.ProofEpochInfo, err = k.ProveEpochInfo(epoch) + if err != nil { + return nil, err + } + + // proof of inclusion for validator set in sealer header + proof.ProofEpochValSet, err = k.ProveValSet(epoch) + if err != nil { + return nil, err + } + + return proof, nil +} + +// ProveEpochSubmitted generates proof that the epoch's checkpoint is submitted to BTC +// i.e., the two `TransactionInfo`s for the checkpoint +func (k Keeper) ProveEpochSubmitted(ctx context.Context, sk *btcctypes.SubmissionKey) ([]*btcctypes.TransactionInfo, error) { + bestSubmissionData := k.btccKeeper.GetSubmissionData(ctx, *sk) + if bestSubmissionData == nil { + return nil, fmt.Errorf("the best submission key for epoch %d has no submission data", bestSubmissionData.Epoch) + } + return bestSubmissionData.TxsInfo, nil +} + +// proveFinalizedChainInfo generates proofs that a chainInfo has been finalised by the given epoch with epochInfo +// It includes proofTxInBlock, proofHeaderInEpoch, proofEpochSealed and proofEpochSubmitted +// The proofs can be verified by a verifier with access to a BTC and Babylon light client +// CONTRACT: this is only a private helper function for simplifying the implementation of RPC calls +func (k Keeper) proveFinalizedChainInfo( + ctx context.Context, + chainInfo *types.ChainInfo, + epochInfo *epochingtypes.Epoch, + bestSubmissionKey *btcctypes.SubmissionKey, +) (*types.ProofFinalizedChainInfo, error) { + var ( + err error + proof = &types.ProofFinalizedChainInfo{} + ) + + // Proof that the CZ header is timestamped in epoch + proof.ProofCzHeaderInEpoch, err = k.ProveCZHeaderInEpoch(ctx, chainInfo.LatestHeader, epochInfo) + if err != nil { + return nil, err + } + + // proof that the epoch is sealed + proof.ProofEpochSealed, err = k.ProveEpochSealed(ctx, epochInfo.EpochNumber) + 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 + proof.ProofEpochSubmitted, err = k.ProveEpochSubmitted(ctx, bestSubmissionKey) + if err != nil { + // The only error in ProveEpochSubmitted is the nil bestSubmission. + // Since the epoch w.r.t. the bestSubmissionKey is finalised, this + // can only be a programming error, so we should panic here. + panic(err) + } + + return proof, nil +} diff --git a/x/zoneconcierge/keeper/proof_btc_timestamp_test.go b/x/zoneconcierge/keeper/proof_btc_timestamp_test.go new file mode 100644 index 000000000..48190e086 --- /dev/null +++ b/x/zoneconcierge/keeper/proof_btc_timestamp_test.go @@ -0,0 +1,292 @@ +package keeper_test + +import ( + "encoding/hex" + "math/rand" + "testing" + + "github.com/boljen/go-bitmap" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" +) + +func FuzzProofCZHeaderInEpoch(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + h := testhelper.NewHelper(t) + ek := h.App.EpochingKeeper + zck := h.App.ZoneConciergeKeeper + var err error + + // chain is at height 1 thus epoch 1 + + // enter the 1st block of epoch 2 + epochInterval := ek.GetParams(h.Ctx).EpochInterval + for j := 0; j < int(epochInterval)-1; j++ { + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + + // handle a random header from a random consumer chain + chainID := datagen.GenRandomHexStr(r, 10) + height := datagen.RandomInt(r, 100) + 1 + ibctmHeader := datagen.GenRandomIBCTMHeader(r, chainID, height) + headerInfo := datagen.HeaderToHeaderInfo(ibctmHeader) + zck.HandleHeaderWithValidCommit(h.Ctx, datagen.GenRandomByteArray(r, 32), headerInfo, false) + + // ensure the header is successfully inserted + indexedHeader, err := zck.GetHeader(h.Ctx, chainID, height) + h.NoError(err) + + // enter the 1st block of the next epoch + for j := 0; j < int(epochInterval); j++ { + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + + epochWithHeader, err := ek.GetHistoricalEpoch(h.Ctx, indexedHeader.BabylonEpoch) + h.NoError(err) + + // generate inclusion proof + proof, err := zck.ProveCZHeaderInEpoch(h.Ctx, indexedHeader, epochWithHeader) + h.NoError(err) + + // verify the inclusion proof + err = zctypes.VerifyCZHeaderInEpoch(indexedHeader, epochWithHeader, proof) + h.NoError(err) + }) +} + +func signBLSWithBitmap(blsSKs []bls12381.PrivateKey, bm bitmap.Bitmap, msg []byte) (bls12381.Signature, error) { + sigs := []bls12381.Signature{} + for i := 0; i < len(blsSKs); i++ { + if bitmap.Get(bm, i) { + sig := bls12381.Sign(blsSKs[i], msg) + sigs = append(sigs, sig) + } + } + return bls12381.AggrSigList(sigs) +} + +// FuzzProofEpochSealed fuzz tests the prover and verifier of ProofEpochSealed +// Process: +// 1. Generate a random epoch that has a legitimate-looking SealerHeader +// 2. Generate a random validator set with BLS PKs +// 3. Generate a BLS multisig with >2/3 random validators of the validator set +// 4. Generate a checkpoint based on the above validator subset and the above sealer header +// 5. Execute ProveEpochSealed where the mocked checkpointing keeper produces the above validator set +// 6. Execute VerifyEpochSealed with above epoch, checkpoint and proof, and assert the outcome to be true +// +// Tested property: proof is valid only when +// - BLS sig in proof is valid +func FuzzProofEpochSealed_BLSSig(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // generate a random epoch + epoch := datagen.GenRandomEpoch(r) + + // generate a random validator set with 100 validators + numVals := 100 + valSet, blsSKs := datagen.GenerateValidatorSetWithBLSPrivKeys(numVals) + + // sample a validator subset, which may or may not reach a quorum + bm, numSubSet := datagen.GenRandomBitmap(r) + _, subsetPower, err := valSet.FindSubsetWithPowerSum(bm) + require.NoError(t, err) + + // construct the rawCkpt + // Note that the BlsMultiSig will be generated and assigned later + blockHash := checkpointingtypes.BlockHash(epoch.SealerBlockHash) + rawCkpt := &checkpointingtypes.RawCheckpoint{ + EpochNum: epoch.EpochNumber, + BlockHash: &blockHash, + Bitmap: bm, + BlsMultiSig: nil, + } + + // let the subset generate a BLS multisig over sealer header's app_hash + multiSig, err := signBLSWithBitmap(blsSKs, bm, rawCkpt.SignedMsg()) + require.NoError(t, err) + // assign multiSig to rawCkpt + rawCkpt.BlsMultiSig = &multiSig + + // mock checkpointing keeper that produces the expected validator set + checkpointingKeeper := zctypes.NewMockCheckpointingKeeper(ctrl) + checkpointingKeeper.EXPECT().GetBLSPubKeySet(gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return(valSet.ValSet, nil).AnyTimes() + // mock epoching keeper + epochingKeeper := zctypes.NewMockEpochingKeeper(ctrl) + 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, nil, checkpointingKeeper, nil, epochingKeeper) + + // prove + proof, err := zcKeeper.ProveEpochSealed(ctx, epoch.EpochNumber) + require.NoError(t, err) + // verify + err = zctypes.VerifyEpochSealed(epoch, rawCkpt, proof) + + if subsetPower*3 <= valSet.GetTotalPower()*2 { // BLS sig does not reach a quorum + require.LessOrEqual(t, numSubSet*3, numVals*2) + require.Error(t, err) + require.NotErrorIs(t, err, zctypes.ErrInvalidMerkleProof) + } else { // BLS sig has a valid quorum + require.Greater(t, numSubSet*3, numVals*2) + require.Error(t, err) + require.ErrorIs(t, err, zctypes.ErrInvalidMerkleProof) + } + }) +} + +func FuzzProofEpochSealed_Epoch(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + h := testhelper.NewHelper(t) + ek := h.App.EpochingKeeper + zck := h.App.ZoneConciergeKeeper + var err error + + // chain is at height 1 + + // enter the 1st block of a random epoch + epochInterval := ek.GetParams(h.Ctx).EpochInterval + newEpochs := datagen.RandomInt(r, 10) + 2 + for i := 0; i < int(newEpochs); i++ { + for j := 0; j < int(epochInterval); j++ { + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + } + + // prove the inclusion of last epoch + lastEpochNumber := ek.GetEpoch(h.Ctx).EpochNumber - 1 + h.NoError(err) + lastEpoch, err := ek.GetHistoricalEpoch(h.Ctx, lastEpochNumber) + h.NoError(err) + proof, err := zck.ProveEpochInfo(lastEpoch) + h.NoError(err) + + // verify inclusion proof + err = zctypes.VerifyEpochInfo(lastEpoch, proof) + h.NoError(err) + }) +} + +func FuzzProofEpochSealed_ValSet(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + h := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ek := h.App.EpochingKeeper + ck := h.App.CheckpointingKeeper + zck := h.App.ZoneConciergeKeeper + + // chain is at height 1 + // enter the 1st block of a random epoch + epochInterval := ek.GetParams(h.Ctx).EpochInterval + newEpochs := datagen.RandomInt(r, 10) + 2 + for i := 0; i < int(newEpochs); i++ { + for j := 0; j < int(epochInterval); j++ { + _, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + } + + // seal the last epoch at the 2nd block of the current epoch + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + + // prove the inclusion of last epoch + lastEpochNumber := ek.GetEpoch(h.Ctx).EpochNumber - 1 + h.NoError(err) + lastEpoch, err := ek.GetHistoricalEpoch(h.Ctx, lastEpochNumber) + h.NoError(err) + lastEpochValSet := ck.GetValidatorBlsKeySet(h.Ctx, lastEpochNumber) + proof, err := zck.ProveValSet(lastEpoch) + h.NoError(err) + + // verify inclusion proof + err = zctypes.VerifyValSet(lastEpoch, lastEpochValSet, proof) + h.NoError(err) + }) +} + +func FuzzProofEpochSubmitted(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate random epoch, random rawBtcCkpt and random rawCkpt + epoch := datagen.GenRandomEpoch(r) + rawBtcCkpt := datagen.GetRandomRawBtcCheckpoint(r) + rawBtcCkpt.Epoch = epoch.EpochNumber + rawCkpt, err := checkpointingtypes.FromBTCCkptToRawCkpt(rawBtcCkpt) + require.NoError(t, err) + + // encode ckpt to BTC txs in BTC blocks + testRawCkptData := datagen.EncodeRawCkptToTestData(rawBtcCkpt) + idxs := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} + offsets := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} + btcBlocks := []*datagen.BlockCreationResult{ + datagen.CreateBlock(r, 1, uint32(idxs[0]+offsets[0]), uint32(idxs[0]), testRawCkptData.FirstPart), + datagen.CreateBlock(r, 2, uint32(idxs[1]+offsets[1]), uint32(idxs[1]), testRawCkptData.SecondPart), + } + // create MsgInsertBtcSpvProof for the rawCkpt + msgInsertBtcSpvProof := datagen.GenerateMessageWithRandomSubmitter([]*datagen.BlockCreationResult{btcBlocks[0], btcBlocks[1]}) + + // get headers for verification + btcHeaders := []*wire.BlockHeader{ + btcBlocks[0].HeaderBytes.ToBlockHeader(), + btcBlocks[1].HeaderBytes.ToBlockHeader(), + } + + // get 2 tx info for the ckpt parts + txsInfo := []*btcctypes.TransactionInfo{ + { + Key: &btcctypes.TransactionKey{Index: uint32(idxs[0]), Hash: btcBlocks[0].HeaderBytes.Hash()}, + Transaction: msgInsertBtcSpvProof.Proofs[0].BtcTransaction, + Proof: msgInsertBtcSpvProof.Proofs[0].MerkleNodes, + }, + { + Key: &btcctypes.TransactionKey{Index: uint32(idxs[1]), Hash: btcBlocks[1].HeaderBytes.Hash()}, + Transaction: msgInsertBtcSpvProof.Proofs[1].BtcTransaction, + Proof: msgInsertBtcSpvProof.Proofs[1].MerkleNodes, + }, + } + + // net param, babylonTag + powLimit := chaincfg.SimNetParams.PowLimit + babylonTag := btcctypes.DefaultCheckpointTag + tagAsBytes, _ := hex.DecodeString(babylonTag) + + // verify + err = zctypes.VerifyEpochSubmitted(rawCkpt, txsInfo, btcHeaders, powLimit, tagAsBytes) + require.NoError(t, err) + }) +} diff --git a/x/zoneconcierge/keeper/proof_epoch_sealed.go b/x/zoneconcierge/keeper/proof_epoch_sealed.go deleted file mode 100644 index a0d32e1a8..000000000 --- a/x/zoneconcierge/keeper/proof_epoch_sealed.go +++ /dev/null @@ -1,170 +0,0 @@ -package keeper - -import ( - "fmt" - - errorsmod "cosmossdk.io/errors" - "github.com/babylonchain/babylon/crypto/bls12381" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - "github.com/babylonchain/babylon/x/zoneconcierge/types" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func getEpochInfoKey(epochNumber uint64) []byte { - epochInfoKey := epochingtypes.EpochInfoKey - epochInfoKey = append(epochInfoKey, sdk.Uint64ToBigEndian(epochNumber)...) - return epochInfoKey -} - -func (k Keeper) ProveEpochInfo(epoch *epochingtypes.Epoch) (*tmcrypto.ProofOps, error) { - epochInfoKey := getEpochInfoKey(epoch.EpochNumber) - _, _, proof, err := k.QueryStore(epochingtypes.StoreKey, epochInfoKey, epoch.SealerHeader.Height) - if err != nil { - return nil, err - } - - return proof, nil -} - -func getValSetKey(epochNumber uint64) []byte { - valSetKey := checkpointingtypes.ValidatorBlsKeySetPrefix - valSetKey = append(valSetKey, sdk.Uint64ToBigEndian(epochNumber)...) - return valSetKey -} - -func (k Keeper) ProveValSet(epoch *epochingtypes.Epoch) (*tmcrypto.ProofOps, error) { - valSetKey := getValSetKey(epoch.EpochNumber) - _, _, proof, err := k.QueryStore(checkpointingtypes.StoreKey, valSetKey, epoch.SealerHeader.Height) - if err != nil { - return nil, err - } - return proof, nil -} - -// ProveEpochSealed proves an epoch has been sealed, i.e., -// - the epoch's validator set has a valid multisig over the sealer header -// - the epoch's validator set is committed to the sealer header's last_commit_hash -// - the epoch's metadata is committed to the sealer header's last_commit_hash -func (k Keeper) ProveEpochSealed(ctx sdk.Context, epochNumber uint64) (*types.ProofEpochSealed, error) { - var ( - proof = &types.ProofEpochSealed{} - err error - ) - - // get the validator set of the sealed epoch - proof.ValidatorSet, err = k.checkpointingKeeper.GetBLSPubKeySet(ctx, epochNumber) - if err != nil { - return nil, err - } - - // get sealer header and the query height - epoch, err := k.epochingKeeper.GetHistoricalEpoch(ctx, epochNumber) - if err != nil { - return nil, err - } - - // proof of inclusion for epoch metadata in sealer header - proof.ProofEpochInfo, err = k.ProveEpochInfo(epoch) - if err != nil { - return nil, err - } - - // proof of inclusion for validator set in sealer header - proof.ProofEpochValSet, err = k.ProveValSet(epoch) - if err != nil { - return nil, err - } - - return proof, nil -} - -// VerifyEpochSealed verifies that the given `epoch` is sealed by the `rawCkpt` by using the given `proof` -// The verification rules include: -// - basic sanity checks -// - The raw checkpoint's last_commit_hash is same as in the header of the sealer epoch -// - More than 1/3 (in voting power) validators in the validator set of this epoch have signed last_commit_hash of the sealer epoch -// - The epoch medatata is committed to the app_hash of the sealer epoch -// - The validator set is committed to the app_hash of the sealer epoch -func VerifyEpochSealed(epoch *epochingtypes.Epoch, rawCkpt *checkpointingtypes.RawCheckpoint, proof *types.ProofEpochSealed) error { - // nil check - if epoch == nil { - return fmt.Errorf("epoch is nil") - } else if rawCkpt == nil { - return fmt.Errorf("rawCkpt is nil") - } else if proof == nil { - return fmt.Errorf("proof is nil") - } - - // sanity check - if err := epoch.ValidateBasic(); err != nil { - return err - } else if err := rawCkpt.ValidateBasic(); err != nil { - return err - } else if err = proof.ValidateBasic(); err != nil { - return err - } - - // ensure epoch number is same in epoch and rawCkpt - if epoch.EpochNumber != rawCkpt.EpochNum { - return fmt.Errorf("epoch.EpochNumber (%d) is not equal to rawCkpt.EpochNum (%d)", epoch.EpochNumber, rawCkpt.EpochNum) - } - - // ensure the raw checkpoint's last_commit_hash is same as in the header of the sealer header - // NOTE: since this proof is assembled by a Babylon node who has verified the checkpoint, - // the two lch values should always be the same, otherwise this Babylon node is malicious. - // This is different from the checkpoint verification rules in checkpointing, - // where a checkpoint with valid BLS multisig but different lch signals a dishonest majority equivocation. - lchInCkpt := rawCkpt.LastCommitHash - lchInSealerHeader := checkpointingtypes.LastCommitHash(epoch.SealerHeader.LastCommitHash) - if !lchInCkpt.Equal(lchInSealerHeader) { - return fmt.Errorf("LastCommitHash is not same in rawCkpt (%s) and epoch's SealerHeader (%s)", lchInCkpt.String(), lchInSealerHeader.String()) - } - - /* - Ensure more than 1/3 (in voting power) validators of this epoch have signed (epoch_num || last_commit_hash) in the raw checkpoint - */ - valSet := checkpointingtypes.ValidatorWithBlsKeySet{ValSet: proof.ValidatorSet} - // filter validator set that contributes to the signature - signerSet, signerSetPower, err := valSet.FindSubsetWithPowerSum(rawCkpt.Bitmap) - if err != nil { - return err - } - // ensure the signerSet has > 1/3 voting power - if signerSetPower <= valSet.GetTotalPower()*1/3 { - return fmt.Errorf("the BLS signature involves insufficient voting power") - } - // verify BLS multisig - signedMsgBytes := rawCkpt.SignedMsg() - ok, err := bls12381.VerifyMultiSig(*rawCkpt.BlsMultiSig, signerSet.GetBLSKeySet(), signedMsgBytes) - if err != nil { - return err - } - if !ok { - return fmt.Errorf("BLS signature does not match the public key") - } - - // get the Merkle root, i.e., the AppHash of the sealer header - root := epoch.SealerHeader.AppHash - - // Ensure The epoch medatata is committed to the app_hash of the sealer header - epochBytes, err := epoch.Marshal() - if err != nil { - return err - } - if err := VerifyStore(root, epochingtypes.StoreKey, getEpochInfoKey(epoch.EpochNumber), epochBytes, proof.ProofEpochInfo); err != nil { - return errorsmod.Wrapf(types.ErrInvalidMerkleProof, "invalid inclusion proof for epoch metadata: %v", err) - } - - // Ensure The validator set is committed to the app_hash of the sealer header - valSetBytes, err := valSet.Marshal() - if err != nil { - return err - } - if err := VerifyStore(root, checkpointingtypes.StoreKey, getValSetKey(epoch.EpochNumber), valSetBytes, proof.ProofEpochValSet); err != nil { - return errorsmod.Wrapf(types.ErrInvalidMerkleProof, "invalid inclusion proof for validator set: %v", err) - } - - return nil -} diff --git a/x/zoneconcierge/keeper/proof_epoch_sealed_test.go b/x/zoneconcierge/keeper/proof_epoch_sealed_test.go deleted file mode 100644 index 8f6dded9f..000000000 --- a/x/zoneconcierge/keeper/proof_epoch_sealed_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package keeper_test - -import ( - "math/rand" - "testing" - - "github.com/babylonchain/babylon/crypto/bls12381" - "github.com/babylonchain/babylon/testutil/datagen" - testkeeper "github.com/babylonchain/babylon/testutil/keeper" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" - zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" - "github.com/boljen/go-bitmap" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" -) - -func signBLSWithBitmap(blsSKs []bls12381.PrivateKey, bm bitmap.Bitmap, msg []byte) (bls12381.Signature, error) { - sigs := []bls12381.Signature{} - for i := 0; i < len(blsSKs); i++ { - if bitmap.Get(bm, i) { - sig := bls12381.Sign(blsSKs[i], msg) - sigs = append(sigs, sig) - } - } - return bls12381.AggrSigList(sigs) -} - -// FuzzProofEpochSealed fuzz tests the prover and verifier of ProofEpochSealed -// Process: -// 1. Generate a random epoch that has a legitimate-looking SealerHeader -// 2. Generate a random validator set with BLS PKs -// 3. Generate a BLS multisig with >1/3 random validators of the validator set -// 4. Generate a checkpoint based on the above validator subset and the above sealer header -// 5. Execute ProveEpochSealed where the mocked checkpointing keeper produces the above validator set -// 6. Execute VerifyEpochSealed with above epoch, checkpoint and proof, and assert the outcome to be true -// -// Tested property: proof is valid only when -// - BLS sig in proof is valid -func FuzzProofEpochSealed_BLSSig(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // generate a random epoch - epoch := datagen.GenRandomEpoch(r) - - // generate a random validator set with 100 validators - numVals := 100 - valSet, blsSKs := datagen.GenerateValidatorSetWithBLSPrivKeys(numVals) - - // sample a validator subset, which may or may not reach a quorum - bm, numSubSet := datagen.GenRandomBitmap(r) - _, subsetPower, err := valSet.FindSubsetWithPowerSum(bm) - require.NoError(t, err) - - // construct the rawCkpt - // Note that the BlsMultiSig will be generated and assigned later - lch := checkpointingtypes.LastCommitHash(epoch.SealerHeader.LastCommitHash) - rawCkpt := &checkpointingtypes.RawCheckpoint{ - EpochNum: epoch.EpochNumber, - LastCommitHash: &lch, - Bitmap: bm, - BlsMultiSig: nil, - } - - // let the subset generate a BLS multisig over sealer header's last_commit_hash - multiSig, err := signBLSWithBitmap(blsSKs, bm, rawCkpt.SignedMsg()) - require.NoError(t, err) - // assign multiSig to rawCkpt - rawCkpt.BlsMultiSig = &multiSig - - // mock checkpointing keeper that produces the expected validator set - checkpointingKeeper := zctypes.NewMockCheckpointingKeeper(ctrl) - checkpointingKeeper.EXPECT().GetBLSPubKeySet(gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return(valSet.ValSet, nil).AnyTimes() - // mock epoching keeper - epochingKeeper := zctypes.NewMockEpochingKeeper(ctrl) - 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, nil, checkpointingKeeper, nil, epochingKeeper, nil) - - // prove - proof, err := zcKeeper.ProveEpochSealed(ctx, epoch.EpochNumber) - require.NoError(t, err) - // verify - err = zckeeper.VerifyEpochSealed(epoch, rawCkpt, proof) - - if subsetPower <= valSet.GetTotalPower()*1/3 { // BLS sig does not reach a quorum - require.LessOrEqual(t, numSubSet, numVals*1/3) - require.Error(t, err) - require.NotErrorIs(t, err, zctypes.ErrInvalidMerkleProof) - } else { // BLS sig has a valid quorum - require.Greater(t, numSubSet, numVals*1/3) - require.Error(t, err) - require.ErrorIs(t, err, zctypes.ErrInvalidMerkleProof) - } - }) -} - -// - TODO: epoch metadata has a valid inclusion proof -func FuzzProofEpochSealed_Epoch(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - // r := rand.New(rand.NewSource(seed)) - }) -} - -// - TODO: BLS val set has a valid inclusion proof -func FuzzProofEpochSealed_ValSet(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - // r := rand.New(rand.NewSource(seed)) - }) -} diff --git a/x/zoneconcierge/keeper/proof_epoch_submitted.go b/x/zoneconcierge/keeper/proof_epoch_submitted.go deleted file mode 100644 index 758f2fc3e..000000000 --- a/x/zoneconcierge/keeper/proof_epoch_submitted.go +++ /dev/null @@ -1,94 +0,0 @@ -package keeper - -import ( - "fmt" - "math/big" - - txformat "github.com/babylonchain/babylon/btctxformatter" - "github.com/babylonchain/babylon/types" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - "github.com/btcsuite/btcd/wire" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ProveEpochSubmitted generates proof that the epoch's checkpoint is submitted to BTC -// i.e., the two `TransactionInfo`s for the checkpoint -func (k Keeper) ProveEpochSubmitted(ctx sdk.Context, sk *btcctypes.SubmissionKey) ([]*btcctypes.TransactionInfo, error) { - bestSubmissionData := k.btccKeeper.GetSubmissionData(ctx, *sk) - if bestSubmissionData == nil { - return nil, fmt.Errorf("the best submission key for epoch %d has no submission data", bestSubmissionData.Epoch) - } - return bestSubmissionData.TxsInfo, nil -} - -// VerifyEpochSubmitted verifies whether an epoch's checkpoint has been included in BTC or not -// verifications include: -// - basic sanity checks -// - Merkle proofs in txsInfo are valid -// - the raw ckpt decoded from txsInfo is same as the expected rawCkpt -func VerifyEpochSubmitted(rawCkpt *checkpointingtypes.RawCheckpoint, txsInfo []*btcctypes.TransactionInfo, btcHeaders []*wire.BlockHeader, powLimit *big.Int, babylonTag txformat.BabylonTag) error { - // basic sanity check - if rawCkpt == nil { - return fmt.Errorf("rawCkpt is nil") - } else if len(txsInfo) != txformat.NumberOfParts { - return fmt.Errorf("txsInfo contains %d parts rather than %d", len(txsInfo), txformat.NumberOfParts) - } else if len(btcHeaders) != txformat.NumberOfParts { - return fmt.Errorf("btcHeaders contains %d parts rather than %d", len(btcHeaders), txformat.NumberOfParts) - } - - // sanity check of each tx info - for _, txInfo := range txsInfo { - if err := txInfo.ValidateBasic(); err != nil { - return err - } - } - - // verify Merkle proofs for each tx info - parsedProofs := []*btcctypes.ParsedProof{} - for i, txInfo := range txsInfo { - btcHeaderBytes := types.NewBTCHeaderBytesFromBlockHeader(btcHeaders[i]) - parsedProof, err := btcctypes.ParseProof( - txInfo.Transaction, - txInfo.Key.Index, - txInfo.Proof, - &btcHeaderBytes, - powLimit, - ) - if err != nil { - return err - } - parsedProofs = append(parsedProofs, parsedProof) - } - - // decode parsedProof to checkpoint data - checkpointData := [][]byte{} - for i, proof := range parsedProofs { - data, err := txformat.GetCheckpointData( - babylonTag, - txformat.CurrentVersion, - uint8(i), - proof.OpReturnData, - ) - - if err != nil { - return err - } - checkpointData = append(checkpointData, data) - } - rawCkptData, err := txformat.ConnectParts(txformat.CurrentVersion, checkpointData[0], checkpointData[1]) - if err != nil { - return err - } - decodedRawCkpt, err := checkpointingtypes.FromBTCCkptBytesToRawCkpt(rawCkptData) - if err != nil { - return err - } - - // check if decodedRawCkpt is same as the expected rawCkpt - if !decodedRawCkpt.Equal(rawCkpt) { - return fmt.Errorf("the decoded rawCkpt (%v) is different from the expected rawCkpt (%v)", decodedRawCkpt, rawCkpt) - } - - return nil -} diff --git a/x/zoneconcierge/keeper/proof_epoch_submitted_test.go b/x/zoneconcierge/keeper/proof_epoch_submitted_test.go deleted file mode 100644 index c8cacc3c5..000000000 --- a/x/zoneconcierge/keeper/proof_epoch_submitted_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package keeper_test - -import ( - "encoding/hex" - "math/rand" - "testing" - - txformat "github.com/babylonchain/babylon/btctxformatter" - "github.com/babylonchain/babylon/testutil/datagen" - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" - zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/stretchr/testify/require" -) - -func FuzzProofEpochSubmitted(f *testing.F) { - datagen.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - // generate random epoch, random rawBtcCkpt and random rawCkpt - epoch := datagen.GenRandomEpoch(r) - rawBtcCkpt := datagen.GetRandomRawBtcCheckpoint(r) - rawBtcCkpt.Epoch = epoch.EpochNumber - rawCkpt, err := checkpointingtypes.FromBTCCkptToRawCkpt(rawBtcCkpt) - require.NoError(t, err) - - // encode ckpt to BTC txs in BTC blocks - testRawCkptData := datagen.EncodeRawCkptToTestData(rawBtcCkpt) - idxs := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} - offsets := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} - btcBlocks := []*datagen.BlockCreationResult{ - datagen.CreateBlock(r, 1, uint32(idxs[0]+offsets[0]), uint32(idxs[0]), testRawCkptData.FirstPart), - datagen.CreateBlock(r, 2, uint32(idxs[1]+offsets[1]), uint32(idxs[1]), testRawCkptData.SecondPart), - } - // create MsgInsertBtcSpvProof for the rawCkpt - msgInsertBtcSpvProof := datagen.GenerateMessageWithRandomSubmitter([]*datagen.BlockCreationResult{btcBlocks[0], btcBlocks[1]}) - - // get headers for verification - btcHeaders := []*wire.BlockHeader{ - btcBlocks[0].HeaderBytes.ToBlockHeader(), - btcBlocks[1].HeaderBytes.ToBlockHeader(), - } - - // get 2 tx info for the ckpt parts - txsInfo := []*btcctypes.TransactionInfo{ - { - Key: &btcctypes.TransactionKey{Index: uint32(idxs[0]), Hash: btcBlocks[0].HeaderBytes.Hash()}, - Transaction: msgInsertBtcSpvProof.Proofs[0].BtcTransaction, - Proof: msgInsertBtcSpvProof.Proofs[0].MerkleNodes, - }, - { - Key: &btcctypes.TransactionKey{Index: uint32(idxs[1]), Hash: btcBlocks[1].HeaderBytes.Hash()}, - Transaction: msgInsertBtcSpvProof.Proofs[1].BtcTransaction, - Proof: msgInsertBtcSpvProof.Proofs[1].MerkleNodes, - }, - } - - // net param, babylonTag - powLimit := chaincfg.SimNetParams.PowLimit - babylonTag := btcctypes.DefaultCheckpointTag - tagAsBytes, _ := hex.DecodeString(babylonTag) - - // verify - err = zckeeper.VerifyEpochSubmitted(rawCkpt, txsInfo, btcHeaders, powLimit, txformat.BabylonTag(tagAsBytes)) - require.NoError(t, err) - }) -} diff --git a/x/zoneconcierge/keeper/proof_finalized_chain_info.go b/x/zoneconcierge/keeper/proof_finalized_chain_info.go deleted file mode 100644 index f8d99fc1c..000000000 --- a/x/zoneconcierge/keeper/proof_finalized_chain_info.go +++ /dev/null @@ -1,58 +0,0 @@ -package keeper - -import ( - btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - "github.com/babylonchain/babylon/x/zoneconcierge/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// proveFinalizedChainInfo generates proofs that a chainInfo has been finalised by the given epoch with epochInfo -// It includes proofTxInBlock, proofHeaderInEpoch, proofEpochSealed and proofEpochSubmitted -// The proofs can be verified by a verifier with access to a BTC and Babylon light client -// CONTRACT: this is only a private helper function for simplifying the implementation of RPC calls -func (k Keeper) proveFinalizedChainInfo( - ctx sdk.Context, - chainInfo *types.ChainInfo, - epochInfo *epochingtypes.Epoch, - bestSubmissionKey *btcctypes.SubmissionKey, -) (*types.ProofFinalizedChainInfo, error) { - var ( - err error - proof = &types.ProofFinalizedChainInfo{} - ) - - // Proof that the Babylon tx is in block - proof.ProofTxInBlock, err = k.ProveTxInBlock(ctx, chainInfo.LatestHeader.BabylonTxHash) - if err != nil { - return nil, err - } - - // proof that the block is in this epoch - proof.ProofHeaderInEpoch, err = k.ProveHeaderInEpoch(ctx, chainInfo.LatestHeader.BabylonHeader, epochInfo) - if err != nil { - return nil, err - } - - // proof that the epoch is sealed - proof.ProofEpochSealed, err = k.ProveEpochSealed(ctx, epochInfo.EpochNumber) - 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 - proof.ProofEpochSubmitted, err = k.ProveEpochSubmitted(ctx, bestSubmissionKey) - if err != nil { - // The only error in ProveEpochSubmitted is the nil bestSubmission. - // Since the epoch w.r.t. the bestSubmissionKey is finalised, this - // can only be a programming error, so we should panic here. - panic(err) - } - - return proof, nil -} - -// TODO: implement a standalone verifier VerifyFinalizedChainInfo that -// verifies whether a chainInfo is finalised or not, with access to -// Bitcoin and Babylon light clients diff --git a/x/zoneconcierge/keeper/proof_tx_in_block.go b/x/zoneconcierge/keeper/proof_tx_in_block.go deleted file mode 100644 index 89b309926..000000000 --- a/x/zoneconcierge/keeper/proof_tx_in_block.go +++ /dev/null @@ -1,34 +0,0 @@ -package keeper - -import ( - "crypto/sha256" - "fmt" - - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (k Keeper) ProveTxInBlock(ctx sdk.Context, txHash []byte) (*tmproto.TxProof, error) { - if len(txHash) != sha256.Size { - return nil, fmt.Errorf("invalid txHash length: expected: %d, actual: %d", sha256.Size, len(txHash)) - } - - // query the tx with inclusion proof - resTx, err := k.tmClient.Tx(ctx, txHash, true) - if err != nil { - return nil, err - } - - txProof := resTx.Proof.ToProto() - return &txProof, nil -} - -func VerifyTxInBlock(txHash []byte, proof *tmproto.TxProof) error { - txProof, err := tmtypes.TxProofFromProto(*proof) - if err != nil { - return err - } - - return txProof.Proof.Verify(txProof.RootHash, txHash) -} diff --git a/x/zoneconcierge/keeper/proof_tx_in_block_test.go b/x/zoneconcierge/keeper/proof_tx_in_block_test.go deleted file mode 100644 index 36d88ce6e..000000000 --- a/x/zoneconcierge/keeper/proof_tx_in_block_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package keeper_test - -import ( - "fmt" - "testing" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/spf13/pflag" - "github.com/stretchr/testify/require" - - "github.com/babylonchain/babylon/app" - zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" -) - -func TestProveTxInBlock(t *testing.T) { - // setup virtual network - min := network.MinimumAppConfig() - cfg, _ := network.DefaultConfigWithAppConfig(min) - encodingCfg := app.GetEncodingConfig() - cfg.InterfaceRegistry = encodingCfg.InterfaceRegistry - cfg.TxConfig = encodingCfg.TxConfig - cfg.NumValidators = 2 - cfg.RPCAddress = "tcp://0.0.0.0:26657" // TODO: parameterise this - testNetwork, err := network.New(t, t.TempDir(), cfg) - require.NoError(t, err) - defer testNetwork.Cleanup() - - // enter block 1 so that gentxs take effect and validators have tokens - err = testNetwork.WaitForNextBlock() - require.NoError(t, err) - - _, babylonChain, _, babylonApp := SetupTest(t) - zcKeeper := babylonApp.ZoneConciergeKeeper - - ctx := babylonChain.GetContext() - - // construct client context - val := testNetwork.Validators[0] - val.ClientCtx.FromAddress = val.Address - val.ClientCtx.FeePayer = val.Address - val.ClientCtx.FeeGranter = val.Address - require.NotEmpty(t, val.Address, val.ValAddress) - fs := pflag.NewFlagSet("", pflag.ContinueOnError) - fs.String(flags.FlagFees, "", "Fees to pay along with transaction; eg: 10ubbn") - fee := fmt.Sprintf("100%s", sdk.DefaultBondDenom) - err = fs.Set(flags.FlagFees, fee) - require.NoError(t, err) - - // construct a simple msg - coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))) - msg := banktypes.NewMsgSend(val.Address, testNetwork.Validators[1].Address, coins) - - txf, err := tx.NewFactoryCLI(val.ClientCtx, fs) - - require.NoError(t, err) - // construct a tx that includes this msg - txf = txf. - WithTxConfig(val.ClientCtx.TxConfig). - WithAccountRetriever(val.ClientCtx.AccountRetriever) - - txf, err = txf.Prepare(val.ClientCtx) - require.NoError(t, err) - txb, err := txf.BuildUnsignedTx(msg) - require.NoError(t, err) - keys, err := val.ClientCtx.Keyring.List() - require.NoError(t, err) - err = tx.Sign(txf.WithKeybase(val.ClientCtx.Keyring), keys[0].Name, txb, true) - require.NoError(t, err) - txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txb.GetTx()) - require.NoError(t, err) - - // submit the tx to Tendermint - resp, err := val.RPCClient.BroadcastTxSync(ctx, txBytes) - require.NoError(t, err) - txHash := resp.Hash - - err = testNetwork.WaitForNextBlock() - require.NoError(t, err) - err = testNetwork.WaitForNextBlock() - require.NoError(t, err) - - proof, err := zcKeeper.ProveTxInBlock(ctx, txHash) - require.NoError(t, err) - - err = zckeeper.VerifyTxInBlock(txHash, proof) - require.NoError(t, err) -} diff --git a/x/zoneconcierge/keeper/query_kvstore.go b/x/zoneconcierge/keeper/query_kvstore.go index bdebccee1..eb1caf149 100644 --- a/x/zoneconcierge/keeper/query_kvstore.go +++ b/x/zoneconcierge/keeper/query_kvstore.go @@ -3,10 +3,8 @@ package keeper import ( "fmt" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/crypto/merkle" - tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "cosmossdk.io/store/types" + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" ) // QueryStore queries a KV pair in the KVStore, where @@ -18,35 +16,25 @@ import ( // - Merkle proof of this KV pair // - error // (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.46.6/baseapp/abci.go#L774-L795) -func (k Keeper) QueryStore(moduleStoreKey string, key []byte, queryHeight int64) ([]byte, []byte, *tmcrypto.ProofOps, error) { +func (k Keeper) QueryStore(moduleStoreKey string, key []byte, queryHeight int64) ([]byte, []byte, *cmtcrypto.ProofOps, error) { // construct the query path for ABCI query // since we are querying the DB directly, the path will not need prefix "/store" as done in ABCIQuery // Instead, it will be formed as "//key", e.g., "/epoching/key" path := fmt.Sprintf("/%s/key", moduleStoreKey) // query the KV with Merkle proof - resp := k.storeQuerier.Query(abci.RequestQuery{ + resp, err := k.storeQuerier.Query(&storetypes.RequestQuery{ Path: path, Data: key, Height: queryHeight - 1, // NOTE: the inclusion proof corresponds to the NEXT header Prove: true, }) + if err != nil { + return nil, nil, nil, err + } if resp.Code != 0 { return nil, nil, nil, fmt.Errorf("query (with path %s) failed with response: %v", path, resp) } return resp.Key, resp.Value, resp.ProofOps, nil } - -// VerifyStore verifies whether a KV pair is committed to the Merkle root, with the assistance of a Merkle proof -// (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.46.6/store/rootmulti/proof_test.go) -func VerifyStore(root []byte, moduleStoreKey string, key []byte, value []byte, proof *tmcrypto.ProofOps) error { - prt := rootmulti.DefaultProofRuntime() - - keypath := merkle.KeyPath{} - keypath = keypath.AppendKey([]byte(moduleStoreKey), merkle.KeyEncodingURL) - keypath = keypath.AppendKey(key, merkle.KeyEncodingURL) - keypathStr := keypath.String() - - return prt.VerifyAbsence(proof, root, keypathStr) // TODO: verify value rather than just existence -} diff --git a/x/zoneconcierge/keeper/query_kvstore_test.go b/x/zoneconcierge/keeper/query_kvstore_test.go deleted file mode 100644 index 7134afadf..000000000 --- a/x/zoneconcierge/keeper/query_kvstore_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package keeper_test - -import ( - "testing" - - epochingtypes "github.com/babylonchain/babylon/x/epoching/types" - zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" -) - -func TestQueryStore(t *testing.T) { - _, babylonChain, _, babylonApp := SetupTest(t) - zcKeeper := babylonApp.ZoneConciergeKeeper - - babylonChain.NextBlock() - babylonChain.NextBlock() - - ctx := babylonChain.GetContext() - - epochQueryKey := append(epochingtypes.EpochInfoKey, sdk.Uint64ToBigEndian(1)...) - // NOTE: QueryStore will use ctx.BlockHeight() - 1 as the height for ABCI query - // This is because the ABCI query will return the inclusion proof corresponding - // to the NEXT block rather than the given block. - key, value, proof, err := zcKeeper.QueryStore(epochingtypes.StoreKey, epochQueryKey, ctx.BlockHeight()) - - require.NoError(t, err) - require.NotNil(t, proof) - require.Equal(t, epochQueryKey, key) - - err = zckeeper.VerifyStore(ctx.BlockHeader().AppHash, epochingtypes.StoreKey, key, value, proof) - require.NoError(t, err) -} diff --git a/x/zoneconcierge/module.go b/x/zoneconcierge/module.go index 26ffaf051..20ab32932 100644 --- a/x/zoneconcierge/module.go +++ b/x/zoneconcierge/module.go @@ -2,6 +2,7 @@ package zoneconcierge import ( "context" + "cosmossdk.io/core/appmodule" "encoding/json" "fmt" @@ -18,13 +19,15 @@ import ( cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - _ porttypes.IBCModule = IBCModule{} + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ porttypes.IBCModule = IBCModule{} ) // ---------------------------------------------------------------------------- @@ -124,14 +127,12 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // InitGenesis performs the module's genesis initialization. It returns no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) { var genState types.GenesisState // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} } // ExportGenesis returns the module's exported genesis state as raw JSON bytes. @@ -144,11 +145,19 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock contains the logic that is automatically triggered at the beginning of each block -func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper, req) +func (am AppModule) BeginBlock(ctx context.Context) error { + return BeginBlocker(ctx, am.keeper) } // EndBlock contains the logic that is automatically triggered at the end of each block -func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { return EndBlocker(ctx, am.keeper) } + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() { // marker +} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() { // marker +} diff --git a/x/zoneconcierge/module_ibc.go b/x/zoneconcierge/module_ibc.go index 074cbc9ad..cee536d91 100644 --- a/x/zoneconcierge/module_ibc.go +++ b/x/zoneconcierge/module_ibc.go @@ -6,11 +6,11 @@ import ( "github.com/babylonchain/babylon/x/zoneconcierge/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - 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" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ) type IBCModule struct { @@ -167,7 +167,7 @@ func (im IBCModule) OnAcknowledgementPacket( // - 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 { + if errJson := types.ModuleCdc.UnmarshalJSON(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) } diff --git a/x/zoneconcierge/module_ibc_packet_test.go b/x/zoneconcierge/module_ibc_packet_test.go deleted file mode 100644 index 790ed1cbf..000000000 --- a/x/zoneconcierge/module_ibc_packet_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package zoneconcierge_test - -import ( - "encoding/json" - "math/rand" - - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v7/testing" - - "github.com/babylonchain/babylon/app" - zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" -) - -// SetupTest creates a coordinator with 2 test chains. -func (suite *ZoneConciergeTestSuite) SetupTestForIBCPackets() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - // replace the first test chain with a Babylon chain - ibctesting.DefaultTestingAppInit = func() (ibctesting.TestingApp, map[string]json.RawMessage) { - babylonApp := app.Setup(suite.T(), false) - suite.zcKeeper = babylonApp.ZoneConciergeKeeper - encCdc := app.GetEncodingConfig() - genesis := app.NewDefaultGenesisState(encCdc.Marshaler) - return babylonApp, genesis - } - babylonChainID := ibctesting.GetChainID(1) - suite.coordinator.Chains[babylonChainID] = ibctesting.NewTestChain(suite.T(), suite.coordinator, babylonChainID) - - suite.babylonChain = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.czChain = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.babylonChain, 2) - suite.coordinator.CommitNBlocks(suite.czChain, 2) -} - -func (suite *ZoneConciergeTestSuite) TestSetChannel() { - suite.SetupTestForIBCPackets() - - path := ibctesting.NewPath(suite.babylonChain, suite.czChain) - - // 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) - - // check for channel to be created on chainA - _, found := suite.babylonChain.App.GetIBCKeeper().ChannelKeeper.GetChannel(suite.babylonChain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - suite.False(found) - - // establish channel - path.SetChannelOrdered() - suite.coordinator.CreateChannels(path) - - storedChannel, found := suite.babylonChain.App.GetIBCKeeper().ChannelKeeper.GetChannel(suite.babylonChain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - expectedCounterparty := channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - - // assert channel states and properties - suite.True(found) - suite.Equal(channeltypes.OPEN, storedChannel.State) - suite.Equal(channeltypes.ORDERED, storedChannel.Ordering) - suite.Equal(expectedCounterparty, storedChannel.Counterparty) - - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) - err = path.EndpointB.UpdateClient() - suite.Require().NoError(err) - - numTests := 10 - for k := 0; k < numTests; k++ { // test suite does not support fuzz tests so we simulate it here - // retrieve the send sequence number in Babylon - nextSeqSend, found := suite.babylonChain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(suite.babylonChain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - suite.True(found) - - // commit some blocks - numBlocks := rand.Intn(10) - for i := 0; i < numBlocks; i++ { - suite.coordinator.CommitBlock(suite.babylonChain) - } - - // retrieve the send sequence number in Babylon again - 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 - // 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 - err = path.EndpointA.UpdateClient() - suite.Require().NoError(err) - err = path.EndpointB.UpdateClient() - suite.Require().NoError(err) - } - -} diff --git a/x/zoneconcierge/module_simulation.go b/x/zoneconcierge/module_simulation.go deleted file mode 100644 index 7bb84f9a4..000000000 --- a/x/zoneconcierge/module_simulation.go +++ /dev/null @@ -1,50 +0,0 @@ -package zoneconcierge - -import ( - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - - simappparams "github.com/babylonchain/babylon/app/params" - "github.com/babylonchain/babylon/testutil/sample" - zoneconciergesimulation "github.com/babylonchain/babylon/x/zoneconcierge/simulation" - "github.com/babylonchain/babylon/x/zoneconcierge/types" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// avoid unused import issue -var ( - _ = sample.AccAddress - _ = zoneconciergesimulation.FindAccount - _ = simappparams.StakePerAccount - _ = simulation.MsgEntryKind - _ = baseapp.Paramspace -) - -// GenerateGenesisState creates a randomized GenState of the module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - accs := make([]string, len(simState.Accounts)) - for i, acc := range simState.Accounts { - accs[i] = acc.Address.String() - } - zoneconciergeGenesis := types.GenesisState{ - PortId: types.PortID, - } - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&zoneconciergeGenesis) -} - -// ProposalContents doesn't return any content functions for governance proposals -func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { - return nil -} - -// RegisterStoreDecoder registers a decoder -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} - -// WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - operations := make([]simtypes.WeightedOperation, 0) - - return operations -} diff --git a/x/zoneconcierge/module_test.go b/x/zoneconcierge/module_test.go deleted file mode 100644 index b0b96e45b..000000000 --- a/x/zoneconcierge/module_test.go +++ /dev/null @@ -1,380 +0,0 @@ -package zoneconcierge_test - -import ( - "encoding/json" - "fmt" - "math/rand" - "testing" - "time" - - tmbytes "github.com/cometbft/cometbft/libs/bytes" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/cosmos-sdk/codec" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - 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" - 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" - ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v7/testing" - ibctestingmock "github.com/cosmos/ibc-go/v7/testing/mock" - "github.com/cosmos/ibc-go/v7/testing/simapp" - "github.com/stretchr/testify/suite" - - "github.com/babylonchain/babylon/app" - zckeeper "github.com/babylonchain/babylon/x/zoneconcierge/keeper" -) - -// ZoneConciergeTestSuite provides a test suite for IBC functionalities in ZoneConcierge -// (adapted from https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/core/02-client/keeper/keeper_test.go) -type ZoneConciergeTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - babylonChain *ibctesting.TestChain - czChain *ibctesting.TestChain - - // System states of the simulated Babylon chain - cdc codec.Codec - ctx sdk.Context - keeper clientexported.ClientKeeper - zcKeeper zckeeper.Keeper - consensusState *ibctmtypes.ConsensusState - header *ibctmtypes.Header - valSet *tmtypes.ValidatorSet - valSetHash tmbytes.HexBytes - privVal tmtypes.PrivValidator - now time.Time - past time.Time - signers map[string]tmtypes.PrivValidator -} - -func TestZoneConciergeTestSuite(t *testing.T) { - suite.Run(t, new(ZoneConciergeTestSuite)) -} - -func (suite *ZoneConciergeTestSuite) SetupTest() { - // set up 2 Test chains with default constructors - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - // replace the first test chain with a Babylon chain - ibctesting.DefaultTestingAppInit = func() (ibctesting.TestingApp, map[string]json.RawMessage) { - babylonApp := app.Setup(suite.T(), false) - suite.zcKeeper = babylonApp.ZoneConciergeKeeper - encCdc := app.GetEncodingConfig() - return babylonApp, app.NewDefaultGenesisState(encCdc.Marshaler) - } - babylonChainID := ibctesting.GetChainID(1) - suite.coordinator.Chains[babylonChainID] = ibctesting.NewTestChain(suite.T(), suite.coordinator, babylonChainID) - - suite.babylonChain = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.czChain = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - - suite.now = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) - suite.past = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - now2 := suite.now.Add(time.Hour) - app := simapp.Setup(false) - - babylonChainHeight := uint64(5) // TODO: find out why it's 5 (any value > 0 is okay) - suite.cdc = app.AppCodec() - suite.ctx = app.BaseApp.NewContext(false, tmproto.Header{Height: int64(babylonChainHeight), ChainID: babylonChainID, Time: now2}) - suite.keeper = app.IBCKeeper.ClientKeeper - suite.privVal = ibctestingmock.NewPV() - - pubKey, err := suite.privVal.GetPubKey() - suite.Require().NoError(err) - - testClientHeightMinus1 := clienttypes.NewHeight(0, babylonChainHeight-1) - - validator := tmtypes.NewValidator(pubKey, 1) - suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - suite.valSetHash = suite.valSet.Hash() - suite.signers = make(map[string]tmtypes.PrivValidator, 1) - suite.signers[validator.Address.String()] = suite.privVal - - testClientHeight := clienttypes.NewHeight(0, babylonChainHeight) - testChainID := ibctesting.GetChainID(2) - suite.header = suite.babylonChain.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, suite.valSet, suite.signers) - suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) - - var validators stakingtypes.Validators - for i := 1; i < 11; i++ { - privVal := ibctestingmock.NewPV() - tmPk, err := privVal.GetPubKey() - suite.Require().NoError(err) - pk, err := cryptocodec.FromTmPubKeyInterface(tmPk) - suite.Require().NoError(err) - val, err := stakingtypes.NewValidator(sdk.ValAddress(pk.Address()), pk, stakingtypes.Description{}) - suite.Require().NoError(err) - - val.Status = stakingtypes.Bonded - val.Tokens = sdk.NewInt(rand.Int63()) - validators = append(validators, val) - - hi := stakingtypes.NewHistoricalInfo(suite.ctx.BlockHeader(), validators, sdk.DefaultPowerReduction) - app.StakingKeeper.SetHistoricalInfo(suite.ctx, int64(i), &hi) - } -} - -// TestUpdateClientTendermint provides tests on verifying different headers from the IBC light client -// (https://github.com/cosmos/ibc-go/blob/v5.0.0/modules/core/02-client/keeper/client_test.go) -func (suite *ZoneConciergeTestSuite) TestUpdateClientTendermint() { - var ( - path *ibctesting.Path - updateHeader *ibctmtypes.Header - ) - - // Must create header creation functions since suite.header gets recreated on each test case - createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctmtypes.Header { - header, err := suite.babylonChain.ConstructUpdateTMClientHeaderWithTrustedHeight(path.EndpointB.Chain, path.EndpointA.ClientID, trustedHeight) - suite.Require().NoError(err) - return header - } - createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctmtypes.Header { - consState, found := suite.babylonChain.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.babylonChain.GetContext(), path.EndpointA.ClientID, trustedHeight) - suite.Require().True(found) - - return suite.czChain.CreateTMClientHeader(suite.czChain.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctmtypes.ConsensusState).Timestamp.Add(time.Second*5), - suite.czChain.Vals, suite.czChain.Vals, suite.czChain.Vals, suite.czChain.Signers) - } - - cases := []struct { - name string - malleate func() - expPass bool - expDishonestMajority bool - }{ - {"valid update", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - trustHeight := clientState.GetLatestHeight().(clienttypes.Height) - - // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) - - updateHeader = createFutureUpdateFn(trustHeight) - }, true, false}, - {"valid past update", func() { - clientState := path.EndpointA.GetClientState() - trustedHeight := clientState.GetLatestHeight().(clienttypes.Height) - - currHeight := suite.czChain.CurrentHeader.Height - fillHeight := clienttypes.NewHeight(clientState.GetLatestHeight().GetRevisionNumber(), uint64(currHeight)) - - // commit a couple blocks to allow client to fill in gaps - suite.coordinator.CommitBlock(suite.czChain) // this height is not filled in yet - suite.coordinator.CommitBlock(suite.czChain) // this height is filled in by the update below - - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) - - // ensure fill height not set - _, found := suite.babylonChain.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.babylonChain.GetContext(), path.EndpointA.ClientID, fillHeight) - suite.Require().False(found) - - // updateHeader will fill in consensus state between prevConsState and suite.consState - // clientState should not be updated - updateHeader = createPastUpdateFn(fillHeight, trustedHeight) - }, true, false}, - {"valid duplicate update", func() { - clientID := path.EndpointA.ClientID - - height1 := clienttypes.NewHeight(1, 1) - - // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.past, - NextValidatorsHash: suite.czChain.Vals.Hash(), - } - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, height1, prevConsState) - - height5 := clienttypes.NewHeight(1, 5) - // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.past.Add(time.Minute), - NextValidatorsHash: suite.czChain.Vals.Hash(), - } - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, height5, nextConsState) - - height3 := clienttypes.NewHeight(1, 3) - // updateHeader will fill in consensus state between prevConsState and suite.consState - // clientState should not be updated - updateHeader = createPastUpdateFn(height3, height1) - // set updateHeader's consensus state in store to create duplicate UpdateClient scenario - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, updateHeader.GetHeight(), updateHeader.ConsensusState()) - }, true, false}, - {"misbehaviour in dishonest majority CZ: conflicting header", func() { - clientID := path.EndpointA.ClientID - - height1 := clienttypes.NewHeight(1, 1) - // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.past, - NextValidatorsHash: suite.czChain.Vals.Hash(), - } - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, height1, prevConsState) - - height5 := clienttypes.NewHeight(1, 5) - // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.past.Add(time.Minute), - NextValidatorsHash: suite.czChain.Vals.Hash(), - } - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, height5, nextConsState) - - height3 := clienttypes.NewHeight(1, 3) - // updateHeader will fill in consensus state between prevConsState and suite.consState - // clientState should not be updated - updateHeader = createPastUpdateFn(height3, height1) - // set conflicting consensus state in store to create misbehaviour scenario - conflictConsState := updateHeader.ConsensusState() - conflictConsState.Root = commitmenttypes.NewMerkleRoot([]byte("conflicting apphash")) - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, updateHeader.GetHeight(), conflictConsState) - }, false, true}, // Babylon modification: fork headers are rejected before being passed to ClientState, and are recorded in the fork index - {"misbehaviour in dishonest majority CZ: monotonic time violation", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - clientID := path.EndpointA.ClientID - trustedHeight := clientState.GetLatestHeight().(clienttypes.Height) - - // store intermediate consensus state at a time greater than updateHeader time - // this will break time monotonicity - incrementedClientHeight := clientState.GetLatestHeight().Increment().(clienttypes.Height) - intermediateConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.coordinator.CurrentTime.Add(2 * time.Hour), - NextValidatorsHash: suite.czChain.Vals.Hash(), - } - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.babylonChain.GetContext(), clientID, incrementedClientHeight, intermediateConsState) - // set iteration key - clientStore := suite.keeper.ClientStore(suite.ctx, clientID) - ibctmtypes.SetIterationKey(clientStore, incrementedClientHeight) - - clientState.LatestHeight = incrementedClientHeight - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.babylonChain.GetContext(), clientID, clientState) - - updateHeader = createFutureUpdateFn(trustedHeight) - }, false, true}, // Babylon modification: non-monotonic headers are rejected before being passed to ClientState, and are recorded in the fork index - {"client state not found", func() { - updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)) - - path.EndpointA.ClientID = ibctesting.InvalidID - }, false, false}, - {"consensus state not found", func() { - clientState := path.EndpointA.GetClientState() - tmClient, ok := clientState.(*ibctmtypes.ClientState) - suite.Require().True(ok) - tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) - - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.babylonChain.GetContext(), path.EndpointA.ClientID, clientState) - updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(clienttypes.Height)) - }, false, false}, - {"client is not active", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - suite.babylonChain.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.babylonChain.GetContext(), path.EndpointA.ClientID, clientState) - updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(clienttypes.Height)) - }, false, false}, - {"invalid header", func() { - updateHeader = createFutureUpdateFn(path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)) - updateHeader.TrustedHeight = updateHeader.TrustedHeight.Increment().(clienttypes.Height) - }, false, false}, - } - - for _, tc := range cases { - suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - suite.SetupTest() - path = ibctesting.NewPath(suite.babylonChain, suite.czChain) - suite.coordinator.SetupClients(path) - - tc.malleate() - - var clientState exported.ClientState - if tc.expPass { - clientState = path.EndpointA.GetClientState() - } - - err := suite.babylonChain.App.GetIBCKeeper().ClientKeeper.UpdateClient(suite.babylonChain.GetContext(), path.EndpointA.ClientID, updateHeader) - - if tc.expPass { - suite.Require().NoError(err, err) - - newClientState := path.EndpointA.GetClientState() - - // Babylon check: Babylon's IBC light client should never freeze - suite.Require().True(newClientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "Babylon's IBC light client should never freeze") - - expConsensusState := &ibctmtypes.ConsensusState{ - Timestamp: updateHeader.GetTime(), - Root: commitmenttypes.NewMerkleRoot(updateHeader.Header.GetAppHash()), - NextValidatorsHash: updateHeader.Header.NextValidatorsHash, - } - - consensusState, found := suite.babylonChain.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.babylonChain.GetContext(), path.EndpointA.ClientID, updateHeader.GetHeight()) - suite.Require().True(found) - - // Determine if clientState should be updated or not - if updateHeader.GetHeight().GT(clientState.GetLatestHeight()) { - // Header Height is greater than clientState latest Height, clientState should be updated with header.GetHeight() - suite.Require().Equal(updateHeader.GetHeight(), newClientState.GetLatestHeight(), "clientstate height did not update") - } else { - // Update will add past consensus state, clientState should not be updated at all - suite.Require().Equal(clientState.GetLatestHeight(), newClientState.GetLatestHeight(), "client state height updated for past header") - } - - suite.Require().NoError(err) - suite.Require().Equal(expConsensusState, consensusState, "consensus state should have been updated on case %s", tc.name) - - /* Extra Babylon checks */ - ctx := suite.babylonChain.GetContext() - czChainID := suite.czChain.ChainID - updateHeaderHeight := uint64(updateHeader.Header.Height) - - // updateHeader should be correctly recorded in chain info indexer - if tc.name != "valid past update" { // we exclude the case of past update since chain info indexer does not record past update - // updateHeader should be correctly recorded in canonical chain indexer - expUpdateHeader, err := suite.zcKeeper.GetHeader(ctx, czChainID, updateHeaderHeight) - suite.Require().NoError(err) - suite.Require().Equal(expUpdateHeader.Hash, updateHeader.Header.LastCommitHash) - suite.Require().Equal(expUpdateHeader.Height, updateHeaderHeight) - // updateHeader should be correctly recorded in chain info indexer - chainInfo, err := suite.zcKeeper.GetChainInfo(ctx, czChainID) - suite.Require().NoError(err) - suite.Require().Equal(chainInfo.LatestHeader.Hash, updateHeader.Header.LastCommitHash) - suite.Require().Equal(chainInfo.LatestHeader.Height, updateHeaderHeight) - } else { - // there should be no header in updateHeaderHeight - _, err := suite.zcKeeper.GetHeader(ctx, czChainID, updateHeaderHeight) - suite.Require().Error(err) - // the latest header in chain info indexer should be the last header - chainInfo, err := suite.zcKeeper.GetChainInfo(ctx, czChainID) - suite.Require().NoError(err) - suite.Require().Equal(chainInfo.LatestHeader.Hash, suite.czChain.LastHeader.Header.LastCommitHash) - suite.Require().Equal(chainInfo.LatestHeader.Height, uint64(suite.czChain.LastHeader.Header.Height)) - } - } else { - if tc.expDishonestMajority { - /* Extra Babylon checks */ - ctx := suite.babylonChain.GetContext() - czChainID := suite.czChain.ChainID - updateHeaderHeight := uint64(updateHeader.Header.Height) - // updateHeader should be correctly recorded in fork indexer - expForks := suite.zcKeeper.GetForks(ctx, czChainID, updateHeaderHeight) - suite.Require().Equal(1, len(expForks.Headers)) - suite.Require().Equal(expForks.Headers[0].Hash, updateHeader.Header.LastCommitHash) - suite.Require().Equal(expForks.Headers[0].Height, updateHeaderHeight) - // updateHeader should be correctly recorded in chain info indexer - chainInfo, err := suite.zcKeeper.GetChainInfo(ctx, czChainID) - suite.Require().NoError(err) - suite.Require().Equal(1, len(chainInfo.LatestForks.Headers)) - suite.Require().Equal(chainInfo.LatestForks.Headers[0].Hash, updateHeader.Header.LastCommitHash) - suite.Require().Equal(chainInfo.LatestForks.Headers[0].Height, updateHeaderHeight) - } else { - suite.Require().Error(err) - } - } - }) - } -} diff --git a/x/zoneconcierge/simulation/helpers.go b/x/zoneconcierge/simulation/helpers.go deleted file mode 100644 index 92c437c0d..000000000 --- a/x/zoneconcierge/simulation/helpers.go +++ /dev/null @@ -1,15 +0,0 @@ -package simulation - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" -) - -// FindAccount find a specific address from an account list -func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { - creator, err := sdk.AccAddressFromBech32(address) - if err != nil { - panic(err) - } - return simtypes.FindAccount(accs, creator) -} diff --git a/x/zoneconcierge/types/btc_timestamp.go b/x/zoneconcierge/types/btc_timestamp.go new file mode 100644 index 000000000..6d520a61e --- /dev/null +++ b/x/zoneconcierge/types/btc_timestamp.go @@ -0,0 +1,337 @@ +package types + +import ( + "context" + "fmt" + "math/big" + + errorsmod "cosmossdk.io/errors" + "github.com/btcsuite/btcd/wire" + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" + + txformat "github.com/babylonchain/babylon/btctxformatter" + "github.com/babylonchain/babylon/crypto/bls12381" + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclckeeper "github.com/babylonchain/babylon/x/btclightclient/keeper" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" +) + +func GetCZHeaderKey(chainID string, height uint64) []byte { + key := CanonicalChainKey + key = append(key, []byte(chainID)...) + key = append(key, sdk.Uint64ToBigEndian(height)...) + return key +} + +func GetEpochInfoKey(epochNumber uint64) []byte { + epochInfoKey := epochingtypes.EpochInfoKey + epochInfoKey = append(epochInfoKey, sdk.Uint64ToBigEndian(epochNumber)...) + return epochInfoKey +} + +func GetValSetKey(epochNumber uint64) []byte { + valSetKey := checkpointingtypes.ValidatorBlsKeySetPrefix + valSetKey = append(valSetKey, sdk.Uint64ToBigEndian(epochNumber)...) + return valSetKey +} + +func VerifyEpochInfo(epoch *epochingtypes.Epoch, proof *cmtcrypto.ProofOps) error { + // get the Merkle root, i.e., the BlockHash of the sealer header + root := epoch.SealerAppHash + + // Ensure The epoch medatata is committed to the app_hash of the sealer header + // NOTE: the proof is generated when sealer header is generated. At that time + // sealer header hash is not given to epoch metadata. Thus we need to clear the + // sealer header hash when verifying the proof. + epoch.SealerAppHash = []byte{} + epochBytes, err := epoch.Marshal() + if err != nil { + return err + } + epoch.SealerAppHash = root + if err := VerifyStore(root, epochingtypes.StoreKey, GetEpochInfoKey(epoch.EpochNumber), epochBytes, proof); err != nil { + return errorsmod.Wrapf(ErrInvalidMerkleProof, "invalid inclusion proof for epoch metadata: %v", err) + } + + return nil +} + +func VerifyValSet(epoch *epochingtypes.Epoch, valSet *checkpointingtypes.ValidatorWithBlsKeySet, proof *cmtcrypto.ProofOps) error { + valSetBytes, err := valSet.Marshal() + if err != nil { + return err + } + if err := VerifyStore(epoch.SealerAppHash, checkpointingtypes.StoreKey, GetValSetKey(epoch.EpochNumber), valSetBytes, proof); err != nil { + return errorsmod.Wrapf(ErrInvalidMerkleProof, "invalid inclusion proof for validator set: %v", err) + } + + return nil +} + +// VerifyEpochSealed verifies that the given `epoch` is sealed by the `rawCkpt` by using the given `proof` +// The verification rules include: +// - basic sanity checks +// - The raw checkpoint's BlockHash is same as the sealer_block_hash of the sealed epoch +// - More than 2/3 (in voting power) validators in the validator set of this epoch have signed sealer_block_hash of the sealed epoch +// - The epoch medatata is committed to the sealer_app_hash of the sealed epoch +// - The validator set is committed to the sealer_app_hash of the sealed epoch +func VerifyEpochSealed(epoch *epochingtypes.Epoch, rawCkpt *checkpointingtypes.RawCheckpoint, proof *ProofEpochSealed) error { + // nil check + if epoch == nil { + return fmt.Errorf("epoch is nil") + } else if rawCkpt == nil { + return fmt.Errorf("rawCkpt is nil") + } else if proof == nil { + return fmt.Errorf("proof is nil") + } + + // sanity check + if err := epoch.ValidateBasic(); err != nil { + return err + } else if err := rawCkpt.ValidateBasic(); err != nil { + return err + } else if err = proof.ValidateBasic(); err != nil { + return err + } + + // ensure epoch number is same in epoch and rawCkpt + if epoch.EpochNumber != rawCkpt.EpochNum { + return fmt.Errorf("epoch.EpochNumber (%d) is not equal to rawCkpt.EpochNum (%d)", epoch.EpochNumber, rawCkpt.EpochNum) + } + + // ensure the raw checkpoint's block_hash is same as the sealer_block_hash of the sealed epoch + // NOTE: since this proof is assembled by a Babylon node who has verified the checkpoint, + // the two blockhash values should always be the same, otherwise this Babylon node is malicious. + // This is different from the checkpoint verification rules in checkpointing, + // where a checkpoint with valid BLS multisig but different blockhashes signals a dishonest majority equivocation. + blockHashInCkpt := rawCkpt.BlockHash + blockHashInSealerHeader := checkpointingtypes.BlockHash(epoch.SealerBlockHash) + if !blockHashInCkpt.Equal(blockHashInSealerHeader) { + return fmt.Errorf("BlockHash is not same in rawCkpt (%s) and epoch's SealerHeader (%s)", blockHashInCkpt.String(), blockHashInSealerHeader.String()) + } + + /* + Ensure more than 2/3 (in voting power) validators of this epoch have signed (epoch_num || block_hash) in the raw checkpoint + */ + valSet := &checkpointingtypes.ValidatorWithBlsKeySet{ValSet: proof.ValidatorSet} + // filter validator set that contributes to the signature + signerSet, signerSetPower, err := valSet.FindSubsetWithPowerSum(rawCkpt.Bitmap) + if err != nil { + return err + } + // ensure the signerSet has > 2/3 voting power + if signerSetPower*3 <= valSet.GetTotalPower()*2 { + return checkpointingtypes.ErrInsufficientVotingPower + } + // verify BLS multisig + signedMsgBytes := rawCkpt.SignedMsg() + ok, err := bls12381.VerifyMultiSig(*rawCkpt.BlsMultiSig, signerSet.GetBLSKeySet(), signedMsgBytes) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("BLS signature does not match the public key") + } + + // Ensure The epoch medatata is committed to the app_hash of the sealer header + if err := VerifyEpochInfo(epoch, proof.ProofEpochInfo); err != nil { + return err + } + + // Ensure The validator set is committed to the app_hash of the sealer header + if err := VerifyValSet(epoch, valSet, proof.ProofEpochValSet); err != nil { + return err + } + + return nil +} + +func VerifyCZHeaderInEpoch(header *IndexedHeader, epoch *epochingtypes.Epoch, proof *cmtcrypto.ProofOps) error { + // nil check + if header == nil { + return fmt.Errorf("header is nil") + } else if epoch == nil { + return fmt.Errorf("epoch is nil") + } else if proof == nil { + return fmt.Errorf("proof is nil") + } + + // sanity check + if err := header.ValidateBasic(); err != nil { + return err + } else if err := epoch.ValidateBasic(); err != nil { + return err + } + + // ensure epoch number is same in epoch and CZ header + if epoch.EpochNumber != header.BabylonEpoch { + return fmt.Errorf("epoch.EpochNumber (%d) is not equal to header.BabylonEpoch (%d)", epoch.EpochNumber, header.BabylonEpoch) + } + + // get the Merkle root, i.e., the BlockHash of the sealer header + root := epoch.SealerAppHash + + // Ensure The header is committed to the BlockHash of the sealer header + headerBytes, err := header.Marshal() + if err != nil { + return err + } + + if err := VerifyStore(root, StoreKey, GetCZHeaderKey(header.ChainId, header.Height), headerBytes, proof); err != nil { + return errorsmod.Wrapf(ErrInvalidMerkleProof, "invalid inclusion proof for CZ header: %v", err) + } + + return nil +} + +// VerifyEpochSubmitted verifies whether an epoch's checkpoint has been included in BTC or not +// verifications include: +// - basic sanity checks +// - Merkle proofs in txsInfo are valid +// - the raw ckpt decoded from txsInfo is same as the expected rawCkpt +func VerifyEpochSubmitted(rawCkpt *checkpointingtypes.RawCheckpoint, txsInfo []*btcctypes.TransactionInfo, btcHeaders []*wire.BlockHeader, powLimit *big.Int, babylonTag txformat.BabylonTag) error { + // basic sanity check + if rawCkpt == nil { + return fmt.Errorf("rawCkpt is nil") + } else if len(txsInfo) != txformat.NumberOfParts { + return fmt.Errorf("txsInfo contains %d parts rather than %d", len(txsInfo), txformat.NumberOfParts) + } else if len(btcHeaders) != txformat.NumberOfParts { + return fmt.Errorf("btcHeaders contains %d parts rather than %d", len(btcHeaders), txformat.NumberOfParts) + } + + // sanity check of each tx info + for _, txInfo := range txsInfo { + if err := txInfo.ValidateBasic(); err != nil { + return err + } + } + + // verify Merkle proofs for each tx info + parsedProofs := []*btcctypes.ParsedProof{} + for i, txInfo := range txsInfo { + btcHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(btcHeaders[i]) + parsedProof, err := btcctypes.ParseProof( + txInfo.Transaction, + txInfo.Key.Index, + txInfo.Proof, + &btcHeaderBytes, + powLimit, + ) + if err != nil { + return err + } + parsedProofs = append(parsedProofs, parsedProof) + } + + // decode parsedProof to checkpoint data + checkpointData := [][]byte{} + for i, proof := range parsedProofs { + data, err := txformat.GetCheckpointData( + babylonTag, + txformat.CurrentVersion, + uint8(i), + proof.OpReturnData, + ) + + if err != nil { + return err + } + checkpointData = append(checkpointData, data) + } + rawCkptData, err := txformat.ConnectParts(txformat.CurrentVersion, checkpointData[0], checkpointData[1]) + if err != nil { + return err + } + decodedRawCkpt, err := checkpointingtypes.FromBTCCkptBytesToRawCkpt(rawCkptData) + if err != nil { + return err + } + + // check if decodedRawCkpt is same as the expected rawCkpt + if !decodedRawCkpt.Equal(rawCkpt) { + return fmt.Errorf("the decoded rawCkpt (%v) is different from the expected rawCkpt (%v)", decodedRawCkpt, rawCkpt) + } + + return nil +} + +func (ts *BTCTimestamp) Verify( + ctx context.Context, + btclcKeeper *btclckeeper.Keeper, + wValue uint64, + ckptTag txformat.BabylonTag, +) error { + // BTC net + btcNet := btclcKeeper.GetBTCNet() + + // verify and insert all BTC headers + headersBytes := []bbn.BTCHeaderBytes{} + for _, headerInfo := range ts.BtcHeaders { + headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(headerInfo.Header.ToBlockHeader()) + headersBytes = append(headersBytes, headerBytes) + } + if err := btclcKeeper.InsertHeaders(ctx, headersBytes); err != nil { + return err + } + + // get BTC headers that include the checkpoint, and ensure at least 1 of them is w-deep + btcHeadersWithCkpt := []*wire.BlockHeader{} + wDeep := false + for _, key := range ts.BtcSubmissionKey.Key { + header := btclcKeeper.GetHeaderByHash(ctx, key.Hash) + if header == nil { + return fmt.Errorf("header corresponding to the inclusion proof is not on BTC light client") + } + btcHeadersWithCkpt = append(btcHeadersWithCkpt, header.Header.ToBlockHeader()) + + depth, err := btclcKeeper.MainChainDepth(ctx, header.Hash) + if err != nil { + return err + } + if depth >= wValue { + wDeep = true + } + } + if !wDeep { + return fmt.Errorf("checkpoint is not w-deep") + } + + // perform stateless checks that do not rely on BTC light client + return ts.VerifyStateless(btcHeadersWithCkpt, btcNet.PowLimit, ckptTag) +} + +func (ts *BTCTimestamp) VerifyStateless( + btcHeadersWithCkpt []*wire.BlockHeader, + powLimit *big.Int, + ckptTag txformat.BabylonTag, +) error { + // ensure raw checkpoint corresponds to the epoch + if ts.EpochInfo.EpochNumber != ts.RawCheckpoint.EpochNum { + return fmt.Errorf("epoch number in epoch metadata and raw checkpoint is not same") + } + + if len(ts.BtcSubmissionKey.Key) != txformat.NumberOfParts { + return fmt.Errorf("incorrect number of txs for a checkpoint") + } + + // verify the checkpoint txs are committed to the two headers + err := VerifyEpochSubmitted(ts.RawCheckpoint, ts.Proof.ProofEpochSubmitted, btcHeadersWithCkpt, powLimit, ckptTag) + if err != nil { + return err + } + + // verify the epoch is sealed + if err := VerifyEpochSealed(ts.EpochInfo, ts.RawCheckpoint, ts.Proof.ProofEpochSealed); err != nil { + return err + } + + // verify CZ header is committed to the epoch + if err := VerifyCZHeaderInEpoch(ts.Header, ts.EpochInfo, ts.Proof.ProofCzHeaderInEpoch); err != nil { + return err + } + + return nil +} diff --git a/x/zoneconcierge/types/btc_timestamp_test.go b/x/zoneconcierge/types/btc_timestamp_test.go new file mode 100644 index 000000000..2f513329d --- /dev/null +++ b/x/zoneconcierge/types/btc_timestamp_test.go @@ -0,0 +1,168 @@ +package types_test + +import ( + "encoding/hex" + "math/rand" + "testing" + + "github.com/boljen/go-bitmap" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" + + txformat "github.com/babylonchain/babylon/btctxformatter" + "github.com/babylonchain/babylon/crypto/bls12381" + "github.com/babylonchain/babylon/testutil/datagen" + testhelper "github.com/babylonchain/babylon/testutil/helper" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + "github.com/babylonchain/babylon/x/zoneconcierge/types" +) + +func signBLSWithBitmap(blsSKs []bls12381.PrivateKey, bm bitmap.Bitmap, msg []byte) (bls12381.Signature, error) { + sigs := []bls12381.Signature{} + for i := 0; i < len(blsSKs); i++ { + if bitmap.Get(bm, i) { + sig := bls12381.Sign(blsSKs[i], msg) + sigs = append(sigs, sig) + } + } + return bls12381.AggrSigList(sigs) +} + +func FuzzBTCTimestamp(f *testing.F) { + datagen.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + // generate the validator set with 10 validators as genesis + genesisValSet, privSigner, err := datagen.GenesisValidatorSetWithPrivSigner(10) + require.NoError(t, err) + h := testhelper.NewHelperWithValSet(t, genesisValSet, privSigner) + ek := &h.App.EpochingKeeper + zck := h.App.ZoneConciergeKeeper + + // empty BTC timestamp + btcTs := &types.BTCTimestamp{} + btcTs.Proof = &types.ProofFinalizedChainInfo{} + + // chain is at height 1 thus epoch 1 + + /* + generate CZ header and its inclusion proof to an epoch + */ + // enter block 11, 1st block of epoch 2 + epochInterval := ek.GetParams(h.Ctx).EpochInterval + for j := 0; j < int(epochInterval)-2; j++ { + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + + // handle a random header from a random consumer chain + chainID := datagen.GenRandomHexStr(r, 10) + height := datagen.RandomInt(r, 100) + 1 + ibctmHeader := datagen.GenRandomIBCTMHeader(r, chainID, height) + headerInfo := datagen.HeaderToHeaderInfo(ibctmHeader) + zck.HandleHeaderWithValidCommit(h.Ctx, datagen.GenRandomByteArray(r, 32), headerInfo, false) + + // ensure the header is successfully inserted + indexedHeader, err := zck.GetHeader(h.Ctx, chainID, height) + h.NoError(err) + + // enter block 21, 1st block of epoch 3 + for j := 0; j < int(epochInterval); j++ { + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + } + // seal last epoch + h.Ctx, err = h.ApplyEmptyBlockWithVoteExtension(r) + h.NoError(err) + + epochWithHeader, err := ek.GetHistoricalEpoch(h.Ctx, indexedHeader.BabylonEpoch) + h.NoError(err) + + // generate inclusion proof + proof, err := zck.ProveCZHeaderInEpoch(h.Ctx, indexedHeader, epochWithHeader) + h.NoError(err) + + btcTs.EpochInfo = epochWithHeader + btcTs.Header = indexedHeader + btcTs.Proof.ProofCzHeaderInEpoch = proof + + /* + seal the epoch and generate ProofEpochSealed + */ + // construct the rawCkpt + // Note that the BlsMultiSig will be generated and assigned later + bm := datagen.GenFullBitmap() + blockHash := checkpointingtypes.BlockHash(epochWithHeader.SealerBlockHash) + rawCkpt := &checkpointingtypes.RawCheckpoint{ + EpochNum: epochWithHeader.EpochNumber, + BlockHash: &blockHash, + Bitmap: bm, + BlsMultiSig: nil, + } + // let the subset generate a BLS multisig over sealer header's app_hash + multiSig, err := signBLSWithBitmap(h.GenValidators.GetBLSPrivKeys(), bm, rawCkpt.SignedMsg()) + require.NoError(t, err) + // assign multiSig to rawCkpt + rawCkpt.BlsMultiSig = &multiSig + + // prove + btcTs.Proof.ProofEpochSealed, err = zck.ProveEpochSealed(h.Ctx, epochWithHeader.EpochNumber) + require.NoError(t, err) + + btcTs.RawCheckpoint = rawCkpt + + /* + forge two BTC headers including the checkpoint + */ + // encode ckpt to BTC txs in BTC blocks + submitterAddr := datagen.GenRandomByteArray(r, txformat.AddressLength) + rawBTCCkpt, err := checkpointingtypes.FromRawCkptToBTCCkpt(rawCkpt, submitterAddr) + h.NoError(err) + testRawCkptData := datagen.EncodeRawCkptToTestData(rawBTCCkpt) + idxs := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} + offsets := []uint64{datagen.RandomInt(r, 5) + 1, datagen.RandomInt(r, 5) + 1} + btcBlocks := []*datagen.BlockCreationResult{ + datagen.CreateBlock(r, 1, uint32(idxs[0]+offsets[0]), uint32(idxs[0]), testRawCkptData.FirstPart), + datagen.CreateBlock(r, 2, uint32(idxs[1]+offsets[1]), uint32(idxs[1]), testRawCkptData.SecondPart), + } + // create MsgInsertBtcSpvProof for the rawCkpt + msgInsertBtcSpvProof := datagen.GenerateMessageWithRandomSubmitter([]*datagen.BlockCreationResult{btcBlocks[0], btcBlocks[1]}) + + // assign BTC submission key and ProofEpochSubmitted + btcTs.BtcSubmissionKey = &btcctypes.SubmissionKey{ + Key: []*btcctypes.TransactionKey{ + &btcctypes.TransactionKey{Index: uint32(idxs[0]), Hash: btcBlocks[0].HeaderBytes.Hash()}, + &btcctypes.TransactionKey{Index: uint32(idxs[1]), Hash: btcBlocks[1].HeaderBytes.Hash()}, + }, + } + btcTs.Proof.ProofEpochSubmitted = []*btcctypes.TransactionInfo{ + { + Key: btcTs.BtcSubmissionKey.Key[0], + Transaction: msgInsertBtcSpvProof.Proofs[0].BtcTransaction, + Proof: msgInsertBtcSpvProof.Proofs[0].MerkleNodes, + }, + { + Key: btcTs.BtcSubmissionKey.Key[1], + Transaction: msgInsertBtcSpvProof.Proofs[1].BtcTransaction, + Proof: msgInsertBtcSpvProof.Proofs[1].MerkleNodes, + }, + } + + // get headers for verification + btcHeaders := []*wire.BlockHeader{ + btcBlocks[0].HeaderBytes.ToBlockHeader(), + btcBlocks[1].HeaderBytes.ToBlockHeader(), + } + + // net param, babylonTag + powLimit := chaincfg.SimNetParams.PowLimit + babylonTag := btcctypes.DefaultCheckpointTag + tagAsBytes, _ := hex.DecodeString(babylonTag) + + err = btcTs.VerifyStateless(btcHeaders, powLimit, tagAsBytes) + h.NoError(err) + }) +} diff --git a/x/zoneconcierge/types/codec.go b/x/zoneconcierge/types/codec.go index be4f24671..c88ab8585 100644 --- a/x/zoneconcierge/types/codec.go +++ b/x/zoneconcierge/types/codec.go @@ -7,11 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/msgservice" ) -func RegisterCodec(cdc *codec.LegacyAmino) { -} +func RegisterCodec(_ *codec.LegacyAmino) {} func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { - msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/x/zoneconcierge/types/errors.go b/x/zoneconcierge/types/errors.go index 88e178199..f37cae374 100644 --- a/x/zoneconcierge/types/errors.go +++ b/x/zoneconcierge/types/errors.go @@ -1,28 +1,20 @@ package types -// DONTCOVER - import ( errorsmod "cosmossdk.io/errors" ) // 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") - ErrFinalizingBTCTipNotFound = errorsmod.Register(ModuleName, 1115, "cannot find a finalizing BTC tip") - ErrInvalidChainIDs = errorsmod.Register(ModuleName, 1116, "chain ids contain duplicates or empty strings") + ErrInvalidVersion = errorsmod.Register(ModuleName, 1101, "invalid version") + ErrHeaderNotFound = errorsmod.Register(ModuleName, 1102, "no header exists at this height") + ErrInvalidHeader = errorsmod.Register(ModuleName, 1103, "input header is invalid") + ErrChainInfoNotFound = errorsmod.Register(ModuleName, 1104, "no chain info exists") + ErrEpochChainInfoNotFound = errorsmod.Register(ModuleName, 1105, "no chain info exists at this epoch") + ErrEpochHeadersNotFound = errorsmod.Register(ModuleName, 1106, "no timestamped header exists at this epoch") + ErrFinalizedEpochNotFound = errorsmod.Register(ModuleName, 1107, "cannot find a finalized epoch") + ErrInvalidProofEpochSealed = errorsmod.Register(ModuleName, 1108, "invalid ProofEpochSealed") + ErrInvalidMerkleProof = errorsmod.Register(ModuleName, 1109, "invalid Merkle inclusion proof") + ErrInvalidChainInfo = errorsmod.Register(ModuleName, 1110, "invalid chain info") + ErrInvalidChainIDs = errorsmod.Register(ModuleName, 1111, "chain ids contain duplicates or empty strings") ) diff --git a/x/zoneconcierge/types/events_ibc.go b/x/zoneconcierge/types/events_ibc.go index fd4e2bba8..07b499e5a 100644 --- a/x/zoneconcierge/types/events_ibc.go +++ b/x/zoneconcierge/types/events_ibc.go @@ -2,10 +2,8 @@ package types // IBC events const ( - EventTypeAck = "acknowledgement" - EventTypeTimeout = "timeout" + EventTypeAck = "acknowledgement" AttributeKeyAckSuccess = "success" - AttributeKeyAck = "acknowledgement" AttributeKeyAckError = "error" ) diff --git a/x/zoneconcierge/types/expected_keepers.go b/x/zoneconcierge/types/expected_keepers.go index 69d22c16b..303c584ce 100644 --- a/x/zoneconcierge/types/expected_keepers.go +++ b/x/zoneconcierge/types/expected_keepers.go @@ -1,37 +1,36 @@ package types import ( - context "context" + "context" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + bbn "github.com/babylonchain/babylon/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" //nolint:staticcheck 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" ctypes "github.com/cometbft/cometbft/rpc/core/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ) // AccountKeeper defines the contract required for account APIs. type AccountKeeper interface { GetModuleAddress(name string) sdk.AccAddress - GetModuleAccount(ctx sdk.Context, name string) types.ModuleAccountI + GetModuleAccount(ctx context.Context, name string) sdk.ModuleAccountI } // BankKeeper defines the expected bank keeper type BankKeeper interface { - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error BlockedAddr(addr sdk.AccAddress) bool } @@ -58,7 +57,8 @@ type ChannelKeeper interface { // ClientKeeper defines the expected IBC client keeper type ClientKeeper interface { - GetClientConsensusState(ctx sdk.Context, clientID string) (connection ibcexported.ConsensusState, found bool) + GetClientState(ctx sdk.Context, clientID string) (ibcexported.ClientState, bool) + SetClientState(ctx sdk.Context, clientID string, clientState ibcexported.ClientState) } // ConnectionKeeper defines the expected IBC connection keeper @@ -80,33 +80,31 @@ type ScopedKeeper interface { } 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 + GetTipInfo(ctx context.Context) *btclctypes.BTCHeaderInfo + GetMainChainFrom(ctx context.Context, startHeight uint64) []*btclctypes.BTCHeaderInfo + GetMainChainUpTo(ctx context.Context, depth uint64) []*btclctypes.BTCHeaderInfo + GetHeaderByHash(ctx context.Context, hash *bbn.BTCHeaderHashBytes) *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 + GetParams(ctx context.Context) (p btcctypes.Params) + GetEpochData(ctx context.Context, e uint64) *btcctypes.EpochData + GetBestSubmission(ctx context.Context, e uint64) (btcctypes.BtcStatus, *btcctypes.SubmissionKey, error) + GetSubmissionData(ctx context.Context, sk btcctypes.SubmissionKey) *btcctypes.SubmissionData + GetEpochBestSubmissionBtcInfo(ctx context.Context, ed *btcctypes.EpochData) *btcctypes.SubmissionBtcInfo } type CheckpointingKeeper interface { - GetBLSPubKeySet(ctx sdk.Context, epochNumber uint64) ([]*checkpointingtypes.ValidatorWithBlsKey, error) - GetRawCheckpoint(ctx sdk.Context, epochNumber uint64) (*checkpointingtypes.RawCheckpointWithMeta, error) + GetBLSPubKeySet(ctx context.Context, epochNumber uint64) ([]*checkpointingtypes.ValidatorWithBlsKey, error) + GetRawCheckpoint(ctx context.Context, epochNumber uint64) (*checkpointingtypes.RawCheckpointWithMeta, error) } type EpochingKeeper interface { - GetHistoricalEpoch(ctx sdk.Context, epochNumber uint64) (*epochingtypes.Epoch, error) - GetEpoch(ctx sdk.Context) *epochingtypes.Epoch - ProveAppHashInEpoch(ctx sdk.Context, height uint64, epochNumber uint64) (*tmcrypto.Proof, error) + GetHistoricalEpoch(ctx context.Context, epochNumber uint64) (*epochingtypes.Epoch, error) + GetEpoch(ctx context.Context) *epochingtypes.Epoch } -// TMClient is a Tendermint client that allows to query tx inclusion proofs -type TMClient interface { +// CometClient is a Comet client that allows to query tx inclusion proofs +type CometClient interface { Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) } diff --git a/x/zoneconcierge/types/genesis.go b/x/zoneconcierge/types/genesis.go index 37b63a085..1bf65e10d 100644 --- a/x/zoneconcierge/types/genesis.go +++ b/x/zoneconcierge/types/genesis.go @@ -1,12 +1,9 @@ package types import ( - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ) -// DefaultIndex is the default global index -const DefaultIndex uint64 = 1 - // DefaultGenesis returns the default genesis state func DefaultGenesis() *GenesisState { return &GenesisState{ diff --git a/x/zoneconcierge/types/keys.go b/x/zoneconcierge/types/keys.go index b8b2bc1da..bafe72112 100644 --- a/x/zoneconcierge/types/keys.go +++ b/x/zoneconcierge/types/keys.go @@ -1,7 +1,7 @@ package types import ( - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ) const ( @@ -28,14 +28,14 @@ const ( ) 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 - FinalizingBTCTipKey = []byte{0x17} // FinalizingBTCTipKey defines the key to store the BTC tip when the last epoch is finalised - ParamsKey = []byte{0x18} // key prefix for the parameters + 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 + LastSentBTCSegmentKey = []byte{0x17} // LastSentBTCSegmentKey is key holding last btc light client segment sent to other cosmos zones + ParamsKey = []byte{0x18} // key prefix for the parameters ) func KeyPrefix(p string) []byte { diff --git a/x/zoneconcierge/types/mocked_keepers.go b/x/zoneconcierge/types/mocked_keepers.go index 8cd850397..16f242bc4 100644 --- a/x/zoneconcierge/types/mocked_keepers.go +++ b/x/zoneconcierge/types/mocked_keepers.go @@ -8,19 +8,18 @@ import ( context "context" reflect "reflect" - types "github.com/babylonchain/babylon/x/btccheckpoint/types" - 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" + types "github.com/babylonchain/babylon/types" + types0 "github.com/babylonchain/babylon/x/btccheckpoint/types" + types1 "github.com/babylonchain/babylon/x/btclightclient/types" + types2 "github.com/babylonchain/babylon/x/checkpointing/types" + types3 "github.com/babylonchain/babylon/x/epoching/types" 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" + types4 "github.com/cosmos/cosmos-sdk/types" + types5 "github.com/cosmos/ibc-go/modules/capability/types" + types6 "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + types7 "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + types8 "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + exported "github.com/cosmos/ibc-go/v8/modules/core/exported" gomock "github.com/golang/mock/gomock" ) @@ -48,7 +47,7 @@ func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { } // GetModuleAccount mocks base method. -func (m *MockAccountKeeper) GetModuleAccount(ctx types3.Context, name string) types4.ModuleAccountI { +func (m *MockAccountKeeper) GetModuleAccount(ctx context.Context, name string) types4.ModuleAccountI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetModuleAccount", ctx, name) ret0, _ := ret[0].(types4.ModuleAccountI) @@ -62,10 +61,10 @@ func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, name interface{}) } // GetModuleAddress mocks base method. -func (m *MockAccountKeeper) GetModuleAddress(name string) types3.AccAddress { +func (m *MockAccountKeeper) GetModuleAddress(name string) types4.AccAddress { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetModuleAddress", name) - ret0, _ := ret[0].(types3.AccAddress) + ret0, _ := ret[0].(types4.AccAddress) return ret0 } @@ -99,7 +98,7 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { } // BlockedAddr mocks base method. -func (m *MockBankKeeper) BlockedAddr(addr types3.AccAddress) bool { +func (m *MockBankKeeper) BlockedAddr(addr types4.AccAddress) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BlockedAddr", addr) ret0, _ := ret[0].(bool) @@ -113,7 +112,7 @@ func (mr *MockBankKeeperMockRecorder) BlockedAddr(addr interface{}) *gomock.Call } // BurnCoins mocks base method. -func (m *MockBankKeeper) BurnCoins(ctx types3.Context, moduleName string, amt types3.Coins) error { +func (m *MockBankKeeper) BurnCoins(ctx context.Context, moduleName string, amt types4.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BurnCoins", ctx, moduleName, amt) ret0, _ := ret[0].(error) @@ -127,7 +126,7 @@ func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, moduleName, amt interface{} } // MintCoins mocks base method. -func (m *MockBankKeeper) MintCoins(ctx types3.Context, moduleName string, amt types3.Coins) error { +func (m *MockBankKeeper) MintCoins(ctx context.Context, moduleName string, amt types4.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MintCoins", ctx, moduleName, amt) ret0, _ := ret[0].(error) @@ -141,7 +140,7 @@ func (mr *MockBankKeeperMockRecorder) MintCoins(ctx, moduleName, amt interface{} } // SendCoins mocks base method. -func (m *MockBankKeeper) SendCoins(ctx types3.Context, fromAddr, toAddr types3.AccAddress, amt types3.Coins) error { +func (m *MockBankKeeper) SendCoins(ctx context.Context, fromAddr, toAddr types4.AccAddress, amt types4.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt) ret0, _ := ret[0].(error) @@ -155,7 +154,7 @@ func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt inter } // SendCoinsFromAccountToModule mocks base method. -func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types3.Context, senderAddr types3.AccAddress, recipientModule string, amt types3.Coins) error { +func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types4.AccAddress, recipientModule string, amt types4.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) ret0, _ := ret[0].(error) @@ -169,7 +168,7 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAd } // SendCoinsFromModuleToAccount mocks base method. -func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types3.Context, senderModule string, recipientAddr types3.AccAddress, amt types3.Coins) error { +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types4.AccAddress, amt types4.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) ret0, _ := ret[0].(error) @@ -206,7 +205,7 @@ func (m *MockICS4Wrapper) EXPECT() *MockICS4WrapperMockRecorder { } // SendPacket mocks base method. -func (m *MockICS4Wrapper) SendPacket(ctx types3.Context, channelCap *types5.Capability, sourcePort, sourceChannel string, timeoutHeight types6.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { +func (m *MockICS4Wrapper) SendPacket(ctx types4.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, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) ret0, _ := ret[0].(uint64) @@ -244,7 +243,7 @@ func (m *MockChannelKeeper) EXPECT() *MockChannelKeeperMockRecorder { } // GetAllChannels mocks base method. -func (m *MockChannelKeeper) GetAllChannels(ctx types3.Context) []types8.IdentifiedChannel { +func (m *MockChannelKeeper) GetAllChannels(ctx types4.Context) []types8.IdentifiedChannel { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllChannels", ctx) ret0, _ := ret[0].([]types8.IdentifiedChannel) @@ -258,7 +257,7 @@ func (mr *MockChannelKeeperMockRecorder) GetAllChannels(ctx interface{}) *gomock } // GetChannel mocks base method. -func (m *MockChannelKeeper) GetChannel(ctx types3.Context, srcPort, srcChan string) (types8.Channel, bool) { +func (m *MockChannelKeeper) GetChannel(ctx types4.Context, srcPort, srcChan string) (types8.Channel, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChannel", ctx, srcPort, srcChan) ret0, _ := ret[0].(types8.Channel) @@ -273,7 +272,7 @@ func (mr *MockChannelKeeperMockRecorder) GetChannel(ctx, srcPort, srcChan interf } // GetChannelClientState mocks base method. -func (m *MockChannelKeeper) GetChannelClientState(ctx types3.Context, portID, channelID string) (string, exported.ClientState, error) { +func (m *MockChannelKeeper) GetChannelClientState(ctx types4.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) @@ -289,7 +288,7 @@ func (mr *MockChannelKeeperMockRecorder) GetChannelClientState(ctx, portID, chan } // GetNextSequenceSend mocks base method. -func (m *MockChannelKeeper) GetNextSequenceSend(ctx types3.Context, portID, channelID string) (uint64, bool) { +func (m *MockChannelKeeper) GetNextSequenceSend(ctx types4.Context, portID, channelID string) (uint64, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNextSequenceSend", ctx, portID, channelID) ret0, _ := ret[0].(uint64) @@ -326,19 +325,31 @@ func (m *MockClientKeeper) EXPECT() *MockClientKeeperMockRecorder { return m.recorder } -// GetClientConsensusState mocks base method. -func (m *MockClientKeeper) GetClientConsensusState(ctx types3.Context, clientID string) (exported.ConsensusState, bool) { +// GetClientState mocks base method. +func (m *MockClientKeeper) GetClientState(ctx types4.Context, clientID string) (exported.ClientState, bool) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClientConsensusState", ctx, clientID) - ret0, _ := ret[0].(exported.ConsensusState) + ret := m.ctrl.Call(m, "GetClientState", ctx, clientID) + ret0, _ := ret[0].(exported.ClientState) ret1, _ := ret[1].(bool) return ret0, ret1 } -// GetClientConsensusState indicates an expected call of GetClientConsensusState. -func (mr *MockClientKeeperMockRecorder) GetClientConsensusState(ctx, clientID interface{}) *gomock.Call { +// GetClientState indicates an expected call of GetClientState. +func (mr *MockClientKeeperMockRecorder) GetClientState(ctx, clientID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientConsensusState", reflect.TypeOf((*MockClientKeeper)(nil).GetClientConsensusState), ctx, clientID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientState", reflect.TypeOf((*MockClientKeeper)(nil).GetClientState), ctx, clientID) +} + +// SetClientState mocks base method. +func (m *MockClientKeeper) SetClientState(ctx types4.Context, clientID string, clientState exported.ClientState) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetClientState", ctx, clientID, clientState) +} + +// SetClientState indicates an expected call of SetClientState. +func (mr *MockClientKeeperMockRecorder) SetClientState(ctx, clientID, clientState interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetClientState", reflect.TypeOf((*MockClientKeeper)(nil).SetClientState), ctx, clientID, clientState) } // MockConnectionKeeper is a mock of ConnectionKeeper interface. @@ -365,7 +376,7 @@ func (m *MockConnectionKeeper) EXPECT() *MockConnectionKeeperMockRecorder { } // GetConnection mocks base method. -func (m *MockConnectionKeeper) GetConnection(ctx types3.Context, connectionID string) (types7.ConnectionEnd, bool) { +func (m *MockConnectionKeeper) GetConnection(ctx types4.Context, connectionID string) (types7.ConnectionEnd, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConnection", ctx, connectionID) ret0, _ := ret[0].(types7.ConnectionEnd) @@ -403,7 +414,7 @@ func (m *MockPortKeeper) EXPECT() *MockPortKeeperMockRecorder { } // BindPort mocks base method. -func (m *MockPortKeeper) BindPort(ctx types3.Context, portID string) *types5.Capability { +func (m *MockPortKeeper) BindPort(ctx types4.Context, portID string) *types5.Capability { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BindPort", ctx, portID) ret0, _ := ret[0].(*types5.Capability) @@ -440,7 +451,7 @@ func (m *MockScopedKeeper) EXPECT() *MockScopedKeeperMockRecorder { } // AuthenticateCapability mocks base method. -func (m *MockScopedKeeper) AuthenticateCapability(ctx types3.Context, cap *types5.Capability, name string) bool { +func (m *MockScopedKeeper) AuthenticateCapability(ctx types4.Context, cap *types5.Capability, name string) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AuthenticateCapability", ctx, cap, name) ret0, _ := ret[0].(bool) @@ -454,7 +465,7 @@ func (mr *MockScopedKeeperMockRecorder) AuthenticateCapability(ctx, cap, name in } // ClaimCapability mocks base method. -func (m *MockScopedKeeper) ClaimCapability(ctx types3.Context, cap *types5.Capability, name string) error { +func (m *MockScopedKeeper) ClaimCapability(ctx types4.Context, cap *types5.Capability, name string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ClaimCapability", ctx, cap, name) ret0, _ := ret[0].(error) @@ -468,7 +479,7 @@ func (mr *MockScopedKeeperMockRecorder) ClaimCapability(ctx, cap, name interface } // GetCapability mocks base method. -func (m *MockScopedKeeper) GetCapability(ctx types3.Context, name string) (*types5.Capability, bool) { +func (m *MockScopedKeeper) GetCapability(ctx types4.Context, name string) (*types5.Capability, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCapability", ctx, name) ret0, _ := ret[0].(*types5.Capability) @@ -483,7 +494,7 @@ func (mr *MockScopedKeeperMockRecorder) GetCapability(ctx, name interface{}) *go } // LookupModules mocks base method. -func (m *MockScopedKeeper) LookupModules(ctx types3.Context, name string) ([]string, *types5.Capability, error) { +func (m *MockScopedKeeper) LookupModules(ctx types4.Context, name string) ([]string, *types5.Capability, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupModules", ctx, name) ret0, _ := ret[0].([]string) @@ -521,53 +532,39 @@ func (m *MockBTCLightClientKeeper) EXPECT() *MockBTCLightClientKeeperMockRecorde return m.recorder } -// GetBaseBTCHeader mocks base method. -func (m *MockBTCLightClientKeeper) GetBaseBTCHeader(ctx types3.Context) *types0.BTCHeaderInfo { +// GetHeaderByHash mocks base method. +func (m *MockBTCLightClientKeeper) GetHeaderByHash(ctx context.Context, hash *types.BTCHeaderHashBytes) *types1.BTCHeaderInfo { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBaseBTCHeader", ctx) - ret0, _ := ret[0].(*types0.BTCHeaderInfo) + ret := m.ctrl.Call(m, "GetHeaderByHash", ctx, hash) + ret0, _ := ret[0].(*types1.BTCHeaderInfo) return ret0 } -// GetBaseBTCHeader indicates an expected call of GetBaseBTCHeader. -func (mr *MockBTCLightClientKeeperMockRecorder) GetBaseBTCHeader(ctx interface{}) *gomock.Call { +// GetHeaderByHash indicates an expected call of GetHeaderByHash. +func (mr *MockBTCLightClientKeeperMockRecorder) GetHeaderByHash(ctx, hash interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseBTCHeader", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetBaseBTCHeader), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHeaderByHash", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetHeaderByHash), ctx, hash) } -// GetHighestCommonAncestor mocks base method. -func (m *MockBTCLightClientKeeper) GetHighestCommonAncestor(ctx types3.Context, header1, header2 *types0.BTCHeaderInfo) *types0.BTCHeaderInfo { +// GetMainChainFrom mocks base method. +func (m *MockBTCLightClientKeeper) GetMainChainFrom(ctx context.Context, startHeight uint64) []*types1.BTCHeaderInfo { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHighestCommonAncestor", ctx, header1, header2) - ret0, _ := ret[0].(*types0.BTCHeaderInfo) + ret := m.ctrl.Call(m, "GetMainChainFrom", ctx, startHeight) + ret0, _ := ret[0].([]*types1.BTCHeaderInfo) return ret0 } -// GetHighestCommonAncestor indicates an expected call of GetHighestCommonAncestor. -func (mr *MockBTCLightClientKeeperMockRecorder) GetHighestCommonAncestor(ctx, header1, header2 interface{}) *gomock.Call { +// GetMainChainFrom indicates an expected call of GetMainChainFrom. +func (mr *MockBTCLightClientKeeperMockRecorder) GetMainChainFrom(ctx, startHeight 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) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMainChainFrom", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetMainChainFrom), ctx, startHeight) } // GetMainChainUpTo mocks base method. -func (m *MockBTCLightClientKeeper) GetMainChainUpTo(ctx types3.Context, depth uint64) []*types0.BTCHeaderInfo { +func (m *MockBTCLightClientKeeper) GetMainChainUpTo(ctx context.Context, depth uint64) []*types1.BTCHeaderInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMainChainUpTo", ctx, depth) - ret0, _ := ret[0].([]*types0.BTCHeaderInfo) + ret0, _ := ret[0].([]*types1.BTCHeaderInfo) return ret0 } @@ -578,10 +575,10 @@ func (mr *MockBTCLightClientKeeperMockRecorder) GetMainChainUpTo(ctx, depth inte } // GetTipInfo mocks base method. -func (m *MockBTCLightClientKeeper) GetTipInfo(ctx types3.Context) *types0.BTCHeaderInfo { +func (m *MockBTCLightClientKeeper) GetTipInfo(ctx context.Context) *types1.BTCHeaderInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTipInfo", ctx) - ret0, _ := ret[0].(*types0.BTCHeaderInfo) + ret0, _ := ret[0].(*types1.BTCHeaderInfo) return ret0 } @@ -615,11 +612,11 @@ func (m *MockBtcCheckpointKeeper) EXPECT() *MockBtcCheckpointKeeperMockRecorder } // GetBestSubmission mocks base method. -func (m *MockBtcCheckpointKeeper) GetBestSubmission(ctx types3.Context, e uint64) (types.BtcStatus, *types.SubmissionKey, error) { +func (m *MockBtcCheckpointKeeper) GetBestSubmission(ctx context.Context, e uint64) (types0.BtcStatus, *types0.SubmissionKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBestSubmission", ctx, e) - ret0, _ := ret[0].(types.BtcStatus) - ret1, _ := ret[1].(*types.SubmissionKey) + ret0, _ := ret[0].(types0.BtcStatus) + ret1, _ := ret[1].(*types0.SubmissionKey) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } @@ -631,10 +628,10 @@ func (mr *MockBtcCheckpointKeeperMockRecorder) GetBestSubmission(ctx, e interfac } // GetEpochBestSubmissionBtcInfo mocks base method. -func (m *MockBtcCheckpointKeeper) GetEpochBestSubmissionBtcInfo(ctx types3.Context, ed *types.EpochData) *types.SubmissionBtcInfo { +func (m *MockBtcCheckpointKeeper) GetEpochBestSubmissionBtcInfo(ctx context.Context, ed *types0.EpochData) *types0.SubmissionBtcInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEpochBestSubmissionBtcInfo", ctx, ed) - ret0, _ := ret[0].(*types.SubmissionBtcInfo) + ret0, _ := ret[0].(*types0.SubmissionBtcInfo) return ret0 } @@ -645,10 +642,10 @@ func (mr *MockBtcCheckpointKeeperMockRecorder) GetEpochBestSubmissionBtcInfo(ctx } // GetEpochData mocks base method. -func (m *MockBtcCheckpointKeeper) GetEpochData(ctx types3.Context, e uint64) *types.EpochData { +func (m *MockBtcCheckpointKeeper) GetEpochData(ctx context.Context, e uint64) *types0.EpochData { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEpochData", ctx, e) - ret0, _ := ret[0].(*types.EpochData) + ret0, _ := ret[0].(*types0.EpochData) return ret0 } @@ -659,10 +656,10 @@ func (mr *MockBtcCheckpointKeeperMockRecorder) GetEpochData(ctx, e interface{}) } // GetParams mocks base method. -func (m *MockBtcCheckpointKeeper) GetParams(ctx types3.Context) types.Params { +func (m *MockBtcCheckpointKeeper) GetParams(ctx context.Context) types0.Params { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetParams", ctx) - ret0, _ := ret[0].(types.Params) + ret0, _ := ret[0].(types0.Params) return ret0 } @@ -673,10 +670,10 @@ func (mr *MockBtcCheckpointKeeperMockRecorder) GetParams(ctx interface{}) *gomoc } // GetSubmissionData mocks base method. -func (m *MockBtcCheckpointKeeper) GetSubmissionData(ctx types3.Context, sk types.SubmissionKey) *types.SubmissionData { +func (m *MockBtcCheckpointKeeper) GetSubmissionData(ctx context.Context, sk types0.SubmissionKey) *types0.SubmissionData { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSubmissionData", ctx, sk) - ret0, _ := ret[0].(*types.SubmissionData) + ret0, _ := ret[0].(*types0.SubmissionData) return ret0 } @@ -710,10 +707,10 @@ func (m *MockCheckpointingKeeper) EXPECT() *MockCheckpointingKeeperMockRecorder } // GetBLSPubKeySet mocks base method. -func (m *MockCheckpointingKeeper) GetBLSPubKeySet(ctx types3.Context, epochNumber uint64) ([]*types1.ValidatorWithBlsKey, error) { +func (m *MockCheckpointingKeeper) GetBLSPubKeySet(ctx context.Context, epochNumber uint64) ([]*types2.ValidatorWithBlsKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBLSPubKeySet", ctx, epochNumber) - ret0, _ := ret[0].([]*types1.ValidatorWithBlsKey) + ret0, _ := ret[0].([]*types2.ValidatorWithBlsKey) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -725,10 +722,10 @@ func (mr *MockCheckpointingKeeperMockRecorder) GetBLSPubKeySet(ctx, epochNumber } // GetRawCheckpoint mocks base method. -func (m *MockCheckpointingKeeper) GetRawCheckpoint(ctx types3.Context, epochNumber uint64) (*types1.RawCheckpointWithMeta, error) { +func (m *MockCheckpointingKeeper) GetRawCheckpoint(ctx context.Context, epochNumber uint64) (*types2.RawCheckpointWithMeta, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRawCheckpoint", ctx, epochNumber) - ret0, _ := ret[0].(*types1.RawCheckpointWithMeta) + ret0, _ := ret[0].(*types2.RawCheckpointWithMeta) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -763,10 +760,10 @@ func (m *MockEpochingKeeper) EXPECT() *MockEpochingKeeperMockRecorder { } // GetEpoch mocks base method. -func (m *MockEpochingKeeper) GetEpoch(ctx types3.Context) *types2.Epoch { +func (m *MockEpochingKeeper) GetEpoch(ctx context.Context) *types3.Epoch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEpoch", ctx) - ret0, _ := ret[0].(*types2.Epoch) + ret0, _ := ret[0].(*types3.Epoch) return ret0 } @@ -777,10 +774,10 @@ func (mr *MockEpochingKeeperMockRecorder) GetEpoch(ctx interface{}) *gomock.Call } // GetHistoricalEpoch mocks base method. -func (m *MockEpochingKeeper) GetHistoricalEpoch(ctx types3.Context, epochNumber uint64) (*types2.Epoch, error) { +func (m *MockEpochingKeeper) GetHistoricalEpoch(ctx context.Context, epochNumber uint64) (*types3.Epoch, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoricalEpoch", ctx, epochNumber) - ret0, _ := ret[0].(*types2.Epoch) + ret0, _ := ret[0].(*types3.Epoch) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -791,46 +788,31 @@ func (mr *MockEpochingKeeperMockRecorder) GetHistoricalEpoch(ctx, epochNumber in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoricalEpoch", reflect.TypeOf((*MockEpochingKeeper)(nil).GetHistoricalEpoch), ctx, epochNumber) } -// ProveAppHashInEpoch mocks base method. -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) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProveAppHashInEpoch indicates an expected call of ProveAppHashInEpoch. -func (mr *MockEpochingKeeperMockRecorder) ProveAppHashInEpoch(ctx, height, epochNumber interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProveAppHashInEpoch", reflect.TypeOf((*MockEpochingKeeper)(nil).ProveAppHashInEpoch), ctx, height, epochNumber) -} - -// MockTMClient is a mock of TMClient interface. -type MockTMClient struct { +// MockCometClient is a mock of CometClient interface. +type MockCometClient struct { ctrl *gomock.Controller - recorder *MockTMClientMockRecorder + recorder *MockCometClientMockRecorder } -// MockTMClientMockRecorder is the mock recorder for MockTMClient. -type MockTMClientMockRecorder struct { - mock *MockTMClient +// MockCometClientMockRecorder is the mock recorder for MockCometClient. +type MockCometClientMockRecorder struct { + mock *MockCometClient } -// NewMockTMClient creates a new mock instance. -func NewMockTMClient(ctrl *gomock.Controller) *MockTMClient { - mock := &MockTMClient{ctrl: ctrl} - mock.recorder = &MockTMClientMockRecorder{mock} +// NewMockCometClient creates a new mock instance. +func NewMockCometClient(ctrl *gomock.Controller) *MockCometClient { + mock := &MockCometClient{ctrl: ctrl} + mock.recorder = &MockCometClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTMClient) EXPECT() *MockTMClientMockRecorder { +func (m *MockCometClient) EXPECT() *MockCometClientMockRecorder { return m.recorder } // Tx mocks base method. -func (m *MockTMClient) Tx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error) { +func (m *MockCometClient) Tx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Tx", ctx, hash, prove) ret0, _ := ret[0].(*coretypes.ResultTx) @@ -839,7 +821,7 @@ func (m *MockTMClient) Tx(ctx context.Context, hash []byte, prove bool) (*corety } // Tx indicates an expected call of Tx. -func (mr *MockTMClientMockRecorder) Tx(ctx, hash, prove interface{}) *gomock.Call { +func (mr *MockCometClientMockRecorder) Tx(ctx, hash, prove interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tx", reflect.TypeOf((*MockTMClient)(nil).Tx), ctx, hash, prove) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tx", reflect.TypeOf((*MockCometClient)(nil).Tx), ctx, hash, prove) } diff --git a/x/zoneconcierge/types/msg.go b/x/zoneconcierge/types/msg.go index fadaf538a..782e3dcc1 100644 --- a/x/zoneconcierge/types/msg.go +++ b/x/zoneconcierge/types/msg.go @@ -1,7 +1,6 @@ package types import ( - errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,22 +8,3 @@ import ( 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 d2f44c32e..6e123e938 100644 --- a/x/zoneconcierge/types/packet.pb.go +++ b/x/zoneconcierge/types/packet.pb.go @@ -122,7 +122,7 @@ type BTCTimestamp struct { // 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 + //Proofs that the header is finalized Proof *ProofFinalizedChainInfo `protobuf:"bytes,6,opt,name=proof,proto3" json:"proof,omitempty"` } diff --git a/x/zoneconcierge/types/tx.pb.go b/x/zoneconcierge/types/tx.pb.go index c42d065d3..032e66afa 100644 --- a/x/zoneconcierge/types/tx.pb.go +++ b/x/zoneconcierge/types/tx.pb.go @@ -37,7 +37,7 @@ type MsgUpdateParams struct { // 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. + // params defines the zoneconcierge parameters to update. // // NOTE: All parameters must be supplied. Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` @@ -135,7 +135,7 @@ func init() { func init() { proto.RegisterFile("babylon/zoneconcierge/v1/tx.proto", fileDescriptor_35e2112d987e4e18) } var fileDescriptor_35e2112d987e4e18 = []byte{ - // 324 bytes of a gzipped FileDescriptorProto + // 331 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, @@ -149,14 +149,14 @@ var fileDescriptor_35e2112d987e4e18 = []byte{ 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, + 0x97, 0x38, 0x9a, 0xd3, 0x82, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x8d, 0xaa, 0xb8, 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, + 0x0c, 0x89, 0x56, 0x0a, 0xb3, 0x54, 0x8a, 0xb5, 0xe1, 0xf9, 0x06, 0x2d, 0x46, 0x27, 0xff, 0x13, + 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, + 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, + 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0x9a, 0x9e, 0x9c, 0x91, 0x98, 0x99, 0x07, 0xe3, 0xe8, 0x57, + 0xa0, 0xc5, 0x46, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0x2a, 0x8c, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x05, 0xf5, 0x03, 0x87, 0x3a, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -171,7 +171,7 @@ 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 updates the zoneconcierge module parameters. UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) } @@ -194,7 +194,7 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts // MsgServer is the server API for Msg service. type MsgServer interface { - // UpdateParams updates the btccheckpoint module parameters. + // UpdateParams updates the zoneconcierge module parameters. UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) } diff --git a/x/zoneconcierge/types/types.go b/x/zoneconcierge/types/types.go index 6e95fb34e..6795ce58c 100644 --- a/x/zoneconcierge/types/types.go +++ b/x/zoneconcierge/types/types.go @@ -1,5 +1,7 @@ package types +import "time" + // IsLatestHeader checks if a given header is higher than the latest header in chain info func (ci *ChainInfo) IsLatestHeader(header *IndexedHeader) bool { if ci.LatestHeader != nil && ci.LatestHeader.Height > header.Height { @@ -7,3 +9,11 @@ func (ci *ChainInfo) IsLatestHeader(header *IndexedHeader) bool { } return true } + +type HeaderInfo struct { + ClientId string + ChainId string + AppHash []byte + Height uint64 + Time time.Time +} diff --git a/x/zoneconcierge/types/zoneconcierge.go b/x/zoneconcierge/types/zoneconcierge.go index 230715dd0..0a052a01a 100644 --- a/x/zoneconcierge/types/zoneconcierge.go +++ b/x/zoneconcierge/types/zoneconcierge.go @@ -3,8 +3,35 @@ package types import ( "bytes" "fmt" + + "cosmossdk.io/store/rootmulti" + "github.com/cometbft/cometbft/crypto/merkle" + cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" ) +// VerifyStore verifies whether a KV pair is committed to the Merkle root, with the assistance of a Merkle proof +// (adapted from https://github.com/cosmos/cosmos-sdk/blob/v0.46.6/store/rootmulti/proof_test.go) +func VerifyStore(root []byte, moduleStoreKey string, key []byte, value []byte, proof *cmtcrypto.ProofOps) error { + prt := rootmulti.DefaultProofRuntime() + + keypath := merkle.KeyPath{} + keypath = keypath.AppendKey([]byte(moduleStoreKey), merkle.KeyEncodingURL) + keypath = keypath.AppendKey(key, merkle.KeyEncodingURL) + keypathStr := keypath.String() + + // NOTE: the proof can specify verification rules, either only verifying the + // top Merkle root w.r.t. all KV pairs, or verifying every layer of Merkle root + // TODO: investigate how the verification rules are chosen when generating the + // proof + if err1 := prt.VerifyValue(proof, root, keypathStr, value); err1 != nil { + if err2 := prt.VerifyAbsence(proof, root, keypathStr); err2 != nil { + return fmt.Errorf("the Merkle proof does not pass any verification: err of VerifyValue: %w; err of VerifyAbsence: %w", err1, err2) + } + } + + return nil +} + func (p *ProofEpochSealed) ValidateBasic() error { if p.ValidatorSet == nil { return ErrInvalidProofEpochSealed.Wrap("ValidatorSet is nil") @@ -21,11 +48,14 @@ func (p *ProofEpochSealed) ValidateBasic() error { func (ih *IndexedHeader) ValidateBasic() error { if len(ih.ChainId) == 0 { return fmt.Errorf("empty ChainID") - } else if len(ih.Hash) == 0 { + } + if len(ih.Hash) == 0 { return fmt.Errorf("empty Hash") - } else if ih.BabylonHeader == nil { - return fmt.Errorf("nil BabylonHeader") - } else if len(ih.BabylonTxHash) == 0 { + } + if len(ih.BabylonHeaderHash) == 0 { + return fmt.Errorf("empty BabylonHeader hash") + } + if len(ih.BabylonTxHash) == 0 { return fmt.Errorf("empty BabylonTxHash") } return nil @@ -38,13 +68,20 @@ func (ih *IndexedHeader) Equal(ih2 *IndexedHeader) bool { if ih.ChainId != ih2.ChainId { return false - } else if !bytes.Equal(ih.Hash, ih2.Hash) { + } + if !bytes.Equal(ih.Hash, ih2.Hash) { return false - } else if ih.Height != ih2.Height { + } + if ih.Height != ih2.Height { return false - } else if !bytes.Equal(ih.BabylonHeader.LastCommitHash, ih2.BabylonHeader.LastCommitHash) { + } + if !bytes.Equal(ih.BabylonHeaderHash, ih2.BabylonHeaderHash) { + return false + } + if ih.BabylonHeaderHeight != ih2.BabylonHeaderHeight { return false - } else if ih.BabylonEpoch != ih2.BabylonEpoch { + } + if ih.BabylonEpoch != ih2.BabylonEpoch { return false } return bytes.Equal(ih.BabylonTxHash, ih2.BabylonTxHash) diff --git a/x/zoneconcierge/types/zoneconcierge.pb.go b/x/zoneconcierge/types/zoneconcierge.pb.go index c5466c08b..a6b9473e3 100644 --- a/x/zoneconcierge/types/zoneconcierge.pb.go +++ b/x/zoneconcierge/types/zoneconcierge.pb.go @@ -5,11 +5,11 @@ package types import ( fmt "fmt" - types3 "github.com/babylonchain/babylon/x/btccheckpoint/types" - types2 "github.com/babylonchain/babylon/x/checkpointing/types" - types1 "github.com/babylonchain/babylon/x/epoching/types" + types2 "github.com/babylonchain/babylon/x/btccheckpoint/types" + types3 "github.com/babylonchain/babylon/x/btclightclient/types" + types1 "github.com/babylonchain/babylon/x/checkpointing/types" + types "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" @@ -45,15 +45,18 @@ type IndexedHeader struct { // 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 + // babylon_header_hash is the hash of the babylon block that includes this CZ // header - BabylonHeader *types.Header `protobuf:"bytes,5,opt,name=babylon_header,json=babylonHeader,proto3" json:"babylon_header,omitempty"` + BabylonHeaderHash []byte `protobuf:"bytes,5,opt,name=babylon_header_hash,json=babylonHeaderHash,proto3" json:"babylon_header_hash,omitempty"` + // babylon_header_height is the height of the babylon block that includes this CZ + // header + BabylonHeaderHeight uint64 `protobuf:"varint,6,opt,name=babylon_header_height,json=babylonHeaderHeight,proto3" json:"babylon_header_height,omitempty"` // epoch is the epoch number of this header on Babylon ledger - BabylonEpoch uint64 `protobuf:"varint,6,opt,name=babylon_epoch,json=babylonEpoch,proto3" json:"babylon_epoch,omitempty"` + BabylonEpoch uint64 `protobuf:"varint,7,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,7,opt,name=babylon_tx_hash,json=babylonTxHash,proto3" json:"babylon_tx_hash,omitempty"` + BabylonTxHash []byte `protobuf:"bytes,8,opt,name=babylon_tx_hash,json=babylonTxHash,proto3" json:"babylon_tx_hash,omitempty"` } func (m *IndexedHeader) Reset() { *m = IndexedHeader{} } @@ -117,13 +120,20 @@ func (m *IndexedHeader) GetTime() *time.Time { return nil } -func (m *IndexedHeader) GetBabylonHeader() *types.Header { +func (m *IndexedHeader) GetBabylonHeaderHash() []byte { if m != nil { - return m.BabylonHeader + return m.BabylonHeaderHash } return nil } +func (m *IndexedHeader) GetBabylonHeaderHeight() uint64 { + if m != nil { + return m.BabylonHeaderHeight + } + return 0 +} + func (m *IndexedHeader) GetBabylonEpoch() uint64 { if m != nil { return m.BabylonEpoch @@ -281,12 +291,12 @@ type FinalizedChainInfo struct { // finalized_chain_info is the info of the CZ FinalizedChainInfo *ChainInfo `protobuf:"bytes,2,opt,name=finalized_chain_info,json=finalizedChainInfo,proto3" json:"finalized_chain_info,omitempty"` // epoch_info is the metadata of the last BTC-finalised epoch - EpochInfo *types1.Epoch `protobuf:"bytes,3,opt,name=epoch_info,json=epochInfo,proto3" json:"epoch_info,omitempty"` + EpochInfo *types.Epoch `protobuf:"bytes,3,opt,name=epoch_info,json=epochInfo,proto3" json:"epoch_info,omitempty"` // raw_checkpoint is the raw checkpoint of this epoch - RawCheckpoint *types2.RawCheckpoint `protobuf:"bytes,4,opt,name=raw_checkpoint,json=rawCheckpoint,proto3" json:"raw_checkpoint,omitempty"` + RawCheckpoint *types1.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"` + BtcSubmissionKey *types2.SubmissionKey `protobuf:"bytes,5,opt,name=btc_submission_key,json=btcSubmissionKey,proto3" json:"btc_submission_key,omitempty"` // proof is the proof that the chain info is finalized Proof *ProofFinalizedChainInfo `protobuf:"bytes,6,opt,name=proof,proto3" json:"proof,omitempty"` } @@ -338,21 +348,21 @@ func (m *FinalizedChainInfo) GetFinalizedChainInfo() *ChainInfo { return nil } -func (m *FinalizedChainInfo) GetEpochInfo() *types1.Epoch { +func (m *FinalizedChainInfo) GetEpochInfo() *types.Epoch { if m != nil { return m.EpochInfo } return nil } -func (m *FinalizedChainInfo) GetRawCheckpoint() *types2.RawCheckpoint { +func (m *FinalizedChainInfo) GetRawCheckpoint() *types1.RawCheckpoint { if m != nil { return m.RawCheckpoint } return nil } -func (m *FinalizedChainInfo) GetBtcSubmissionKey() *types3.SubmissionKey { +func (m *FinalizedChainInfo) GetBtcSubmissionKey() *types2.SubmissionKey { if m != nil { return m.BtcSubmissionKey } @@ -371,16 +381,16 @@ func (m *FinalizedChainInfo) GetProof() *ProofFinalizedChainInfo { // - Metadata of this epoch, which includes the sealer header // - Raw checkpoint of this epoch // The verifier can perform the following verification rules: -// - The raw checkpoint's `last_commit_hash` is same as in the sealer header -// - More than 1/3 (in voting power) validators in the validator set of this -// epoch have signed `last_commit_hash` of the sealer header +// - The raw checkpoint's `app_hash` is same as in the sealer header +// - More than 2/3 (in voting power) validators in the validator set of this +// epoch have signed `app_hash` of the sealer header // - The epoch medatata is committed to the `app_hash` of the sealer header // - The validator set is committed to the `app_hash` of the sealer header type ProofEpochSealed struct { // validator_set is the validator set of the sealed epoch - // This validator set has generated a BLS multisig on `last_commit_hash` of + // This validator set has generated a BLS multisig on `app_hash` of // the sealer header - ValidatorSet []*types2.ValidatorWithBlsKey `protobuf:"bytes,1,rep,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"` + ValidatorSet []*types1.ValidatorWithBlsKey `protobuf:"bytes,1,rep,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"` // proof_epoch_info is the Merkle proof that the epoch's metadata is committed // to `app_hash` of the sealer header ProofEpochInfo *crypto.ProofOps `protobuf:"bytes,2,opt,name=proof_epoch_info,json=proofEpochInfo,proto3" json:"proof_epoch_info,omitempty"` @@ -422,7 +432,7 @@ func (m *ProofEpochSealed) XXX_DiscardUnknown() { var xxx_messageInfo_ProofEpochSealed proto.InternalMessageInfo -func (m *ProofEpochSealed) GetValidatorSet() []*types2.ValidatorWithBlsKey { +func (m *ProofEpochSealed) GetValidatorSet() []*types1.ValidatorWithBlsKey { if m != nil { return m.ValidatorSet } @@ -446,18 +456,15 @@ func (m *ProofEpochSealed) GetProofEpochValSet() *crypto.ProofOps { // ProofFinalizedChainInfo is a set of proofs that attest a chain info is // BTC-finalised type ProofFinalizedChainInfo struct { - // proof_tx_in_block is the proof that tx that carries the header is included - // in a certain Babylon block - ProofTxInBlock *types.TxProof `protobuf:"bytes,4,opt,name=proof_tx_in_block,json=proofTxInBlock,proto3" json:"proof_tx_in_block,omitempty"` - // proof_header_in_epoch is the proof that the Babylon header is in a certain - // epoch - ProofHeaderInEpoch *crypto.Proof `protobuf:"bytes,5,opt,name=proof_header_in_epoch,json=proofHeaderInEpoch,proto3" json:"proof_header_in_epoch,omitempty"` + // proof_cz_header_in_epoch is the proof that the CZ header is timestamped + // within a certain epoch + ProofCzHeaderInEpoch *crypto.ProofOps `protobuf:"bytes,1,opt,name=proof_cz_header_in_epoch,json=proofCzHeaderInEpoch,proto3" json:"proof_cz_header_in_epoch,omitempty"` // proof_epoch_sealed is the proof that the epoch is sealed - ProofEpochSealed *ProofEpochSealed `protobuf:"bytes,6,opt,name=proof_epoch_sealed,json=proofEpochSealed,proto3" json:"proof_epoch_sealed,omitempty"` + ProofEpochSealed *ProofEpochSealed `protobuf:"bytes,2,opt,name=proof_epoch_sealed,json=proofEpochSealed,proto3" json:"proof_epoch_sealed,omitempty"` // proof_epoch_submitted is the proof that the epoch's checkpoint is included // in BTC ledger It is the two TransactionInfo in the best (i.e., earliest) // checkpoint submission - ProofEpochSubmitted []*types3.TransactionInfo `protobuf:"bytes,7,rep,name=proof_epoch_submitted,json=proofEpochSubmitted,proto3" json:"proof_epoch_submitted,omitempty"` + ProofEpochSubmitted []*types2.TransactionInfo `protobuf:"bytes,3,rep,name=proof_epoch_submitted,json=proofEpochSubmitted,proto3" json:"proof_epoch_submitted,omitempty"` } func (m *ProofFinalizedChainInfo) Reset() { *m = ProofFinalizedChainInfo{} } @@ -493,30 +500,68 @@ func (m *ProofFinalizedChainInfo) XXX_DiscardUnknown() { var xxx_messageInfo_ProofFinalizedChainInfo proto.InternalMessageInfo -func (m *ProofFinalizedChainInfo) GetProofTxInBlock() *types.TxProof { +func (m *ProofFinalizedChainInfo) GetProofCzHeaderInEpoch() *crypto.ProofOps { if m != nil { - return m.ProofTxInBlock + return m.ProofCzHeaderInEpoch } return nil } -func (m *ProofFinalizedChainInfo) GetProofHeaderInEpoch() *crypto.Proof { +func (m *ProofFinalizedChainInfo) GetProofEpochSealed() *ProofEpochSealed { if m != nil { - return m.ProofHeaderInEpoch + return m.ProofEpochSealed } return nil } -func (m *ProofFinalizedChainInfo) GetProofEpochSealed() *ProofEpochSealed { +func (m *ProofFinalizedChainInfo) GetProofEpochSubmitted() []*types2.TransactionInfo { if m != nil { - return m.ProofEpochSealed + return m.ProofEpochSubmitted } return nil } -func (m *ProofFinalizedChainInfo) GetProofEpochSubmitted() []*types3.TransactionInfo { +// Btc light client chain segment grown during last finalized epoch +type BTCChainSegment struct { + BtcHeaders []*types3.BTCHeaderInfo `protobuf:"bytes,1,rep,name=btc_headers,json=btcHeaders,proto3" json:"btc_headers,omitempty"` +} + +func (m *BTCChainSegment) Reset() { *m = BTCChainSegment{} } +func (m *BTCChainSegment) String() string { return proto.CompactTextString(m) } +func (*BTCChainSegment) ProtoMessage() {} +func (*BTCChainSegment) Descriptor() ([]byte, []int) { + return fileDescriptor_ab886e1868e5c5cd, []int{6} +} +func (m *BTCChainSegment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BTCChainSegment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BTCChainSegment.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 *BTCChainSegment) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCChainSegment.Merge(m, src) +} +func (m *BTCChainSegment) XXX_Size() int { + return m.Size() +} +func (m *BTCChainSegment) XXX_DiscardUnknown() { + xxx_messageInfo_BTCChainSegment.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCChainSegment proto.InternalMessageInfo + +func (m *BTCChainSegment) GetBtcHeaders() []*types3.BTCHeaderInfo { if m != nil { - return m.ProofEpochSubmitted + return m.BtcHeaders } return nil } @@ -528,6 +573,7 @@ func init() { proto.RegisterType((*FinalizedChainInfo)(nil), "babylon.zoneconcierge.v1.FinalizedChainInfo") proto.RegisterType((*ProofEpochSealed)(nil), "babylon.zoneconcierge.v1.ProofEpochSealed") proto.RegisterType((*ProofFinalizedChainInfo)(nil), "babylon.zoneconcierge.v1.ProofFinalizedChainInfo") + proto.RegisterType((*BTCChainSegment)(nil), "babylon.zoneconcierge.v1.BTCChainSegment") } func init() { @@ -535,64 +581,65 @@ func init() { } var fileDescriptor_ab886e1868e5c5cd = []byte{ - // 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, + // 928 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdf, 0x6e, 0x1b, 0xc5, + 0x17, 0xce, 0xc6, 0x4e, 0xd2, 0x1c, 0xc7, 0x6d, 0x7e, 0x93, 0xf4, 0xd7, 0x6d, 0x10, 0x8e, 0xe5, + 0x4a, 0xc5, 0x45, 0xb0, 0x96, 0x0d, 0x5c, 0xc0, 0x1d, 0xb6, 0x5a, 0x9a, 0x82, 0x28, 0x5a, 0xbb, + 0x05, 0x21, 0xd0, 0x6a, 0xff, 0x8c, 0x77, 0x57, 0x59, 0xef, 0x58, 0xbb, 0x13, 0x37, 0xce, 0x53, + 0xf4, 0x2d, 0xb8, 0xe6, 0x01, 0xb8, 0xe7, 0xb2, 0x97, 0xdc, 0x81, 0x92, 0x57, 0xe0, 0x86, 0x3b, + 0x34, 0x67, 0x66, 0xd6, 0xbb, 0x8d, 0x4c, 0xe0, 0x26, 0xda, 0x99, 0xf9, 0xce, 0x77, 0xbe, 0xf9, + 0xce, 0x99, 0xe3, 0xc0, 0x07, 0x9e, 0xeb, 0x2d, 0x13, 0x96, 0xf6, 0x2e, 0x58, 0x4a, 0x7d, 0x96, + 0xfa, 0x31, 0xcd, 0x42, 0xda, 0x5b, 0xf4, 0xab, 0x1b, 0xd6, 0x3c, 0x63, 0x9c, 0x11, 0x53, 0xa1, + 0xad, 0xea, 0xe1, 0xa2, 0x7f, 0x74, 0x18, 0xb2, 0x90, 0x21, 0xa8, 0x27, 0xbe, 0x24, 0xfe, 0xe8, + 0x38, 0x64, 0x2c, 0x4c, 0x68, 0x0f, 0x57, 0xde, 0xd9, 0xb4, 0xc7, 0xe3, 0x19, 0xcd, 0xb9, 0x3b, + 0x9b, 0x2b, 0xc0, 0xbb, 0x9c, 0xa6, 0x01, 0xcd, 0x66, 0x71, 0xca, 0x7b, 0x7e, 0xb6, 0x9c, 0x73, + 0x26, 0xb0, 0x6c, 0xaa, 0x8e, 0x0b, 0x75, 0x1e, 0xf7, 0xfd, 0x88, 0xfa, 0xa7, 0x73, 0x26, 0x90, + 0x8b, 0x7e, 0x75, 0x43, 0xa1, 0x1f, 0x6a, 0xf4, 0xea, 0x24, 0x4e, 0x43, 0x44, 0x27, 0xb9, 0x73, + 0x4a, 0x97, 0x0a, 0xf7, 0x68, 0x2d, 0xee, 0x1a, 0x65, 0x47, 0x43, 0xe9, 0x9c, 0xf9, 0x91, 0x42, + 0xe9, 0x6f, 0x85, 0xb1, 0x4a, 0x22, 0x93, 0x38, 0x8c, 0xc4, 0x5f, 0x5a, 0xa8, 0x2c, 0xed, 0x48, + 0x7c, 0xe7, 0x97, 0x4d, 0x68, 0x9e, 0xa4, 0x01, 0x3d, 0xa7, 0xc1, 0x53, 0xea, 0x06, 0x34, 0x23, + 0xf7, 0xe1, 0x96, 0x1f, 0xb9, 0x71, 0xea, 0xc4, 0x81, 0x69, 0xb4, 0x8d, 0xee, 0xae, 0xbd, 0x83, + 0xeb, 0x93, 0x80, 0x10, 0xa8, 0x47, 0x6e, 0x1e, 0x99, 0x9b, 0x6d, 0xa3, 0xbb, 0x67, 0xe3, 0x37, + 0xf9, 0x3f, 0x6c, 0x47, 0x54, 0xd0, 0x9a, 0xb5, 0xb6, 0xd1, 0xad, 0xdb, 0x6a, 0x45, 0x3e, 0x86, + 0xba, 0xf0, 0xd7, 0xac, 0xb7, 0x8d, 0x6e, 0x63, 0x70, 0x64, 0x49, 0xf3, 0x2d, 0x6d, 0xbe, 0x35, + 0xd1, 0xe6, 0x0f, 0xeb, 0xaf, 0x7f, 0x3f, 0x36, 0x6c, 0x44, 0x13, 0x0b, 0x0e, 0xd4, 0x05, 0x9c, + 0x08, 0xe5, 0x38, 0x98, 0x70, 0x0b, 0x13, 0xfe, 0x4f, 0x1d, 0x49, 0xa1, 0x4f, 0x45, 0xf6, 0x01, + 0xdc, 0x7d, 0x1b, 0x2f, 0xc5, 0x6c, 0xa3, 0x98, 0x83, 0x6a, 0x84, 0x54, 0xf6, 0x00, 0x9a, 0x3a, + 0x06, 0xcd, 0x33, 0x77, 0x10, 0xbb, 0xa7, 0x36, 0x1f, 0x8b, 0x3d, 0xf2, 0x10, 0xee, 0x68, 0x10, + 0x3f, 0x97, 0x22, 0x6e, 0xa1, 0x08, 0x1d, 0x3b, 0x39, 0x17, 0x02, 0x3a, 0xcf, 0x60, 0xeb, 0x09, + 0xcb, 0x4e, 0x73, 0xf2, 0x39, 0xec, 0x48, 0x05, 0xb9, 0x59, 0x6b, 0xd7, 0xba, 0x8d, 0xc1, 0x7b, + 0xd6, 0xba, 0xfe, 0xb4, 0x2a, 0x86, 0xdb, 0x3a, 0xae, 0xf3, 0xa7, 0x01, 0xbb, 0x23, 0xb4, 0x3a, + 0x9d, 0xb2, 0x7f, 0xaa, 0xc3, 0x57, 0xd0, 0x4c, 0x5c, 0x4e, 0x73, 0xae, 0x2e, 0x8d, 0x05, 0xf9, + 0x0f, 0x19, 0xf7, 0x64, 0xb4, 0x2a, 0xf8, 0x10, 0xd4, 0xda, 0x99, 0x8a, 0x9b, 0x60, 0x1d, 0x1b, + 0x83, 0xe3, 0xf5, 0x64, 0x78, 0x61, 0xbb, 0x21, 0x83, 0xe4, 0xed, 0x3f, 0x83, 0xfb, 0xc5, 0x6b, + 0xa2, 0x81, 0x92, 0x95, 0x3b, 0x3e, 0x3b, 0x4b, 0x39, 0xb6, 0x40, 0xdd, 0xbe, 0x57, 0x02, 0xc8, + 0xcc, 0xf9, 0x48, 0x1c, 0x77, 0x7e, 0xae, 0x01, 0x79, 0x12, 0xa7, 0x6e, 0x12, 0x5f, 0xd0, 0xe0, + 0x5f, 0xdd, 0xff, 0x05, 0x1c, 0x4e, 0x75, 0x80, 0xa3, 0x40, 0xe9, 0x94, 0x29, 0x1b, 0x1e, 0xac, + 0x57, 0x5e, 0xb0, 0xdb, 0x64, 0x7a, 0x3d, 0xe3, 0xa7, 0x00, 0xd8, 0x10, 0x92, 0xac, 0xa6, 0x1a, + 0x57, 0x93, 0x15, 0x0f, 0x6d, 0xd1, 0xb7, 0xb0, 0x47, 0xec, 0x5d, 0xdc, 0xc2, 0xd0, 0xaf, 0xe1, + 0x76, 0xe6, 0xbe, 0x72, 0x56, 0x4f, 0x56, 0xf5, 0xfd, 0xaa, 0x24, 0x95, 0xe7, 0x2d, 0x38, 0x6c, + 0xf7, 0xd5, 0xa8, 0xd8, 0xb3, 0x9b, 0x59, 0x79, 0x49, 0x5e, 0x00, 0xf1, 0xb8, 0xef, 0xe4, 0x67, + 0xde, 0x2c, 0xce, 0xf3, 0x98, 0xa5, 0x62, 0x62, 0xe0, 0x33, 0x28, 0x73, 0x56, 0xe7, 0xce, 0xa2, + 0x6f, 0x8d, 0x0b, 0xfc, 0x97, 0x74, 0x69, 0xef, 0x7b, 0xdc, 0xaf, 0xec, 0x90, 0x2f, 0x60, 0x0b, + 0x27, 0x1a, 0x3e, 0x8f, 0xc6, 0xa0, 0xbf, 0xde, 0xa9, 0x6f, 0x04, 0xec, 0x7a, 0x55, 0x6c, 0x19, + 0xdf, 0xf9, 0xcb, 0x80, 0x7d, 0x84, 0xa0, 0x13, 0x63, 0xea, 0x26, 0x34, 0x20, 0x36, 0x34, 0x17, + 0x6e, 0x12, 0x07, 0x2e, 0x67, 0x99, 0x93, 0x53, 0x6e, 0x1a, 0xf8, 0x10, 0x3e, 0x5c, 0xef, 0xc1, + 0x4b, 0x0d, 0xff, 0x36, 0xe6, 0xd1, 0x30, 0xc9, 0x85, 0xea, 0xbd, 0x82, 0x63, 0x4c, 0x39, 0x79, + 0x0c, 0xfb, 0x98, 0xd1, 0x29, 0x55, 0x46, 0x96, 0xf9, 0x1d, 0x6b, 0x35, 0xae, 0x2d, 0x39, 0xae, + 0xa5, 0xea, 0xe7, 0xf3, 0xdc, 0xbe, 0x3d, 0x2f, 0xc4, 0x61, 0x7d, 0x9e, 0xc1, 0x41, 0x99, 0x66, + 0xe1, 0x26, 0x28, 0xb0, 0x76, 0x33, 0xd3, 0xfe, 0x8a, 0xe9, 0xa5, 0x9b, 0x8c, 0x29, 0xef, 0xfc, + 0xb4, 0x09, 0xf7, 0xd6, 0xd8, 0x43, 0xc6, 0x60, 0xca, 0x3c, 0xfe, 0x85, 0x1e, 0x48, 0xb1, 0x1e, + 0x33, 0xc6, 0xcd, 0xc9, 0x0e, 0x31, 0x78, 0x74, 0x21, 0xdf, 0xc7, 0x89, 0x9a, 0x45, 0xdf, 0x01, + 0x29, 0x8b, 0xcf, 0xd1, 0x6d, 0xe5, 0xc2, 0xfb, 0x37, 0x94, 0xb0, 0x54, 0x9f, 0xf2, 0x55, 0x54, + 0xc5, 0x7e, 0x84, 0xbb, 0x15, 0x66, 0xd1, 0x2c, 0x9c, 0xd3, 0x40, 0x8d, 0xb0, 0x47, 0xeb, 0x3b, + 0x6d, 0x92, 0xb9, 0x69, 0xee, 0xfa, 0x3c, 0x66, 0xb2, 0x2f, 0x0e, 0x4a, 0xdc, 0x9a, 0xa5, 0xf3, + 0x03, 0xdc, 0x19, 0x4e, 0x46, 0xe8, 0xce, 0x98, 0x86, 0x33, 0x9a, 0x72, 0x72, 0x02, 0x0d, 0xd1, + 0xd8, 0x7a, 0x54, 0xca, 0x0e, 0xe9, 0x96, 0xf3, 0x94, 0x7f, 0xa3, 0x16, 0x7d, 0x6b, 0x38, 0x19, + 0x69, 0x37, 0xa6, 0xcc, 0x06, 0x8f, 0xfb, 0x6a, 0x78, 0x0c, 0x9f, 0xff, 0x7a, 0xd9, 0x32, 0xde, + 0x5c, 0xb6, 0x8c, 0x3f, 0x2e, 0x5b, 0xc6, 0xeb, 0xab, 0xd6, 0xc6, 0x9b, 0xab, 0xd6, 0xc6, 0x6f, + 0x57, 0xad, 0x8d, 0xef, 0x3f, 0x09, 0x63, 0x1e, 0x9d, 0x79, 0x96, 0xcf, 0x66, 0x3d, 0xc5, 0x8c, + 0x53, 0x42, 0x2f, 0x7a, 0xe7, 0x6f, 0xfd, 0x87, 0xc1, 0x97, 0x73, 0x9a, 0x7b, 0xdb, 0xf8, 0xe3, + 0xf4, 0xd1, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x76, 0x46, 0x5d, 0x86, 0x87, 0x08, 0x00, 0x00, } func (m *IndexedHeader) Marshal() (dAtA []byte, err error) { @@ -620,32 +667,32 @@ func (m *IndexedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.BabylonTxHash) i = encodeVarintZoneconcierge(dAtA, i, uint64(len(m.BabylonTxHash))) i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 } if m.BabylonEpoch != 0 { i = encodeVarintZoneconcierge(dAtA, i, uint64(m.BabylonEpoch)) i-- + dAtA[i] = 0x38 + } + if m.BabylonHeaderHeight != 0 { + i = encodeVarintZoneconcierge(dAtA, i, uint64(m.BabylonHeaderHeight)) + i-- dAtA[i] = 0x30 } - if m.BabylonHeader != nil { - { - size, err := m.BabylonHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) - } + if len(m.BabylonHeaderHash) > 0 { + i -= len(m.BabylonHeaderHash) + copy(dAtA[i:], m.BabylonHeaderHash) + i = encodeVarintZoneconcierge(dAtA, i, uint64(len(m.BabylonHeaderHash))) 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 + n1, err1 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.Time):]) + if err1 != nil { + return 0, err1 } - i -= n2 - i = encodeVarintZoneconcierge(dAtA, i, uint64(n2)) + i -= n1 + i = encodeVarintZoneconcierge(dAtA, i, uint64(n1)) i-- dAtA[i] = 0x22 } @@ -949,7 +996,7 @@ func (m *ProofFinalizedChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x1a } } if m.ProofEpochSealed != nil { @@ -962,11 +1009,11 @@ func (m *ProofFinalizedChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x32 + dAtA[i] = 0x12 } - if m.ProofHeaderInEpoch != nil { + if m.ProofCzHeaderInEpoch != nil { { - size, err := m.ProofHeaderInEpoch.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ProofCzHeaderInEpoch.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -974,19 +1021,44 @@ func (m *ProofFinalizedChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x2a + dAtA[i] = 0xa } - if m.ProofTxInBlock != nil { - { - size, err := m.ProofTxInBlock.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + return len(dAtA) - i, nil +} + +func (m *BTCChainSegment) 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 *BTCChainSegment) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BTCChainSegment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + 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 = encodeVarintZoneconcierge(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa } - i-- - dAtA[i] = 0x22 } return len(dAtA) - i, nil } @@ -1023,10 +1095,13 @@ func (m *IndexedHeader) Size() (n int) { l = github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.Time) n += 1 + l + sovZoneconcierge(uint64(l)) } - if m.BabylonHeader != nil { - l = m.BabylonHeader.Size() + l = len(m.BabylonHeaderHash) + if l > 0 { n += 1 + l + sovZoneconcierge(uint64(l)) } + if m.BabylonHeaderHeight != 0 { + n += 1 + sovZoneconcierge(uint64(m.BabylonHeaderHeight)) + } if m.BabylonEpoch != 0 { n += 1 + sovZoneconcierge(uint64(m.BabylonEpoch)) } @@ -1138,12 +1213,8 @@ func (m *ProofFinalizedChainInfo) Size() (n int) { } var l int _ = l - if m.ProofTxInBlock != nil { - l = m.ProofTxInBlock.Size() - n += 1 + l + sovZoneconcierge(uint64(l)) - } - if m.ProofHeaderInEpoch != nil { - l = m.ProofHeaderInEpoch.Size() + if m.ProofCzHeaderInEpoch != nil { + l = m.ProofCzHeaderInEpoch.Size() n += 1 + l + sovZoneconcierge(uint64(l)) } if m.ProofEpochSealed != nil { @@ -1159,6 +1230,21 @@ func (m *ProofFinalizedChainInfo) Size() (n int) { return n } +func (m *BTCChainSegment) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.BtcHeaders) > 0 { + for _, e := range m.BtcHeaders { + l = e.Size() + n += 1 + l + sovZoneconcierge(uint64(l)) + } + } + return n +} + func sovZoneconcierge(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1317,9 +1403,9 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BabylonHeader", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BabylonHeaderHash", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowZoneconcierge @@ -1329,29 +1415,46 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthZoneconcierge } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthZoneconcierge } if postIndex > l { return io.ErrUnexpectedEOF } - if m.BabylonHeader == nil { - m.BabylonHeader = &types.Header{} - } - if err := m.BabylonHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.BabylonHeaderHash = append(m.BabylonHeaderHash[:0], dAtA[iNdEx:postIndex]...) + if m.BabylonHeaderHash == nil { + m.BabylonHeaderHash = []byte{} } iNdEx = postIndex case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BabylonHeaderHeight", wireType) + } + m.BabylonHeaderHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZoneconcierge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BabylonHeaderHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BabylonEpoch", wireType) } @@ -1370,7 +1473,7 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { break } } - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BabylonTxHash", wireType) } @@ -1809,7 +1912,7 @@ func (m *FinalizedChainInfo) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.EpochInfo == nil { - m.EpochInfo = &types1.Epoch{} + m.EpochInfo = &types.Epoch{} } if err := m.EpochInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1845,7 +1948,7 @@ func (m *FinalizedChainInfo) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.RawCheckpoint == nil { - m.RawCheckpoint = &types2.RawCheckpoint{} + m.RawCheckpoint = &types1.RawCheckpoint{} } if err := m.RawCheckpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1881,7 +1984,7 @@ func (m *FinalizedChainInfo) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.BtcSubmissionKey == nil { - m.BtcSubmissionKey = &types3.SubmissionKey{} + m.BtcSubmissionKey = &types2.SubmissionKey{} } if err := m.BtcSubmissionKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2002,7 +2105,7 @@ func (m *ProofEpochSealed) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ValidatorSet = append(m.ValidatorSet, &types2.ValidatorWithBlsKey{}) + m.ValidatorSet = append(m.ValidatorSet, &types1.ValidatorWithBlsKey{}) if err := m.ValidatorSet[len(m.ValidatorSet)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2129,9 +2232,9 @@ func (m *ProofFinalizedChainInfo) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: ProofFinalizedChainInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 4: + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofTxInBlock", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProofCzHeaderInEpoch", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2158,16 +2261,16 @@ func (m *ProofFinalizedChainInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ProofTxInBlock == nil { - m.ProofTxInBlock = &types.TxProof{} + if m.ProofCzHeaderInEpoch == nil { + m.ProofCzHeaderInEpoch = &crypto.ProofOps{} } - if err := m.ProofTxInBlock.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ProofCzHeaderInEpoch.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 5: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofHeaderInEpoch", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProofEpochSealed", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2194,16 +2297,16 @@ func (m *ProofFinalizedChainInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ProofHeaderInEpoch == nil { - m.ProofHeaderInEpoch = &crypto.Proof{} + if m.ProofEpochSealed == nil { + m.ProofEpochSealed = &ProofEpochSealed{} } - if err := m.ProofHeaderInEpoch.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ProofEpochSealed.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 6: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofEpochSealed", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProofEpochSubmitted", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2230,16 +2333,64 @@ func (m *ProofFinalizedChainInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ProofEpochSealed == nil { - m.ProofEpochSealed = &ProofEpochSealed{} - } - if err := m.ProofEpochSealed.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ProofEpochSubmitted = append(m.ProofEpochSubmitted, &types2.TransactionInfo{}) + if err := m.ProofEpochSubmitted[len(m.ProofEpochSubmitted)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 7: + default: + iNdEx = preIndex + skippy, err := skipZoneconcierge(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthZoneconcierge + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BTCChainSegment) 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 ErrIntOverflowZoneconcierge + } + 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: BTCChainSegment: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BTCChainSegment: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofEpochSubmitted", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BtcHeaders", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2266,8 +2417,8 @@ func (m *ProofFinalizedChainInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ProofEpochSubmitted = append(m.ProofEpochSubmitted, &types3.TransactionInfo{}) - if err := m.ProofEpochSubmitted[len(m.ProofEpochSubmitted)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.BtcHeaders = append(m.BtcHeaders, &types3.BTCHeaderInfo{}) + if err := m.BtcHeaders[len(m.BtcHeaders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex