From 42b326a54375b69553fd0889ebca718af03d2c82 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Tue, 23 Mar 2021 18:56:39 +0100 Subject: [PATCH] Added upgrade-chain CLI and updated instructions --- guide/src/upgrade_test.md | 324 +++++++++-------- modules/src/ics07_tendermint/client_state.rs | 20 +- proto-compiler/src/cmd/compile.rs | 1 + proto/src/lib.rs | 5 + proto/src/prost/cosmos.gov.v1beta1.rs | 352 +++++++++++++++++++ relayer-cli/src/commands/tx.rs | 5 + relayer-cli/src/commands/tx/client.rs | 4 +- relayer-cli/src/commands/tx/upgrade.rs | 101 ++++++ relayer/src/lib.rs | 1 + relayer/src/upgrade_chain.rs | 124 +++++++ scripts/one-chain | 2 + 11 files changed, 769 insertions(+), 170 deletions(-) create mode 100644 proto/src/prost/cosmos.gov.v1beta1.rs create mode 100644 relayer-cli/src/commands/tx/upgrade.rs create mode 100644 relayer/src/upgrade_chain.rs diff --git a/guide/src/upgrade_test.md b/guide/src/upgrade_test.md index 49dec35281..aaf06fbb50 100644 --- a/guide/src/upgrade_test.md +++ b/guide/src/upgrade_test.md @@ -1,176 +1,166 @@ ## Prerequisites -- gaiad -``` +- gaiad `(v4.1.*)`, for example: + +```shell $ gaiad version --long | head -n4 name: gaia server_name: gaiad -version: 4.0.0 -commit: a279d091c6f66f8a91c87943139ebaecdd84f689 -``` - -- Go relayer - -```shell -$ rly version -version: 0.8.2 -commit: 489607fa6de093d90fd2f8ac8eb52be3ccf3f145 -cosmos-sdk: v0.41.3 -go: go1.15.6 darwin/amd64 +version: 4.1.2 +commit: 95b07e641d1f69ee12dd911e92b1679f2c64d385 ``` ## Testing procedure -1. Patch the `one-chain` script of the Go relayer. - -```shell -cd ~/go/src/github.com/cosmos/relayer/ -``` - -This patching step is necessary to short-circuit the upgrading of a chain. -With the changes below, a chain will be able to undergo an upgrade within -~200 seconds of the upgrade proposal (instead of the default of 2 days). -With this patch, we can test the upgrade functionality in a matter of minutes. - -Note this only works for MacOS ("Darwin" platform), not tests on Linux. - -```diff -diff --git a/scripts/one-chain b/scripts/one-chain -index d0995fe..3702a88 100755 ---- a/scripts/one-chain -+++ b/scripts/one-chain -@@ -99,6 +99,7 @@ if [ $platform = 'linux' ]; then - sed -i 's/index_all_keys = false/index_all_keys = true/g' $CHAINDIR/$CHAINID/config/config.toml - # sed -i '' 's#index-events = \[\]#index-events = \["message.action","send_packet.packet_src_channel","send_packet.packet_sequence"\]#g' $CHAINDIR/$CHAINID/config/app.toml - else -+ sed -i '' 's#"172800s"#"200s"#g' $CHAINDIR/$CHAINID/config/genesis.json - sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPCPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml - sed -i '' 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml - sed -i '' 's#"localhost:6060"#"localhost:'"$P2PPORT"'"#g' $CHAINDIR/$CHAINID/config/config.toml -``` - -2. Start two gaia instances using the patched developer environment in the Go relayer codebase: - -```shell -./scripts/two-chainz -``` - -3. Setup the Go relayer for these chains: -```shell -$ rly tx link demo -d -o 3s -``` - -Check that everything went fine so far: - -```shell -$ rly paths list - 0: demo -> chns(✔) clnts(✔) conn(✔) chan(✔) (ibc-0:transfer<>ibc-1:transfer) -``` - -4. Create the upgrade plan for chain ibc-0: - -It's important that we parametrize the upgrade plan with a height parameter that -is at least 300 heights ahead of the current height of chain ibc-0. - -First, obtain the current height: -```shell -gaiad query block | jq | grep height - "height": "470", -``` - -Now create the upgrade plan for height 800: -```shell -echo '{ - "Name": "test", - "Height": 800, - "Info": "" -}' > ./upgrade-plan.json -``` - - -5. Submit the upgrade plan - -```shell -rly tx upgrade-chain demo ibc-0 400h 10000000stake ./upgrade-plan.json -``` - -Query for the upgrade plan, check that it was submitted correctly: - -```shell -$ gaiad query gov proposal 1 --home data/ibc-0/ - -content: - '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal - description: upgrade the chain's software and unbonding period - plan: - height: "800" - info: "" - name: test -.... -proposal_id: "1" -status: PROPOSAL_STATUS_VOTING_PERIOD -submit_time: "2021-03-08T13:07:01.417163Z" -total_deposit: -- amount: "10000000" - denom: stake -voting_end_time: "2021-03-08T13:10:21.417163Z" -voting_start_time: "2021-03-08T13:07:01.417163Z" -``` - -6. Vote on the proposal - -The parameter "1" should match the "proposal_id:" from the upgrade proposal -we submitted at step 5. - -```shell -gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator -``` - -Once ibc-0 reaches height 800 (the upgrade height specified in the plan at step 4), the chain should stop executing. - - -7. Initialize and test Hermes - -```shell -cd ~/rust/ibc-rs -``` - - -Patch the developer env of Hermes, to redirect to the correct Gaia directory: -```diff -diff --git a/scripts/init-clients b/scripts/init-clients -index 6cf1a674..bfff9721 100755 ---- a/scripts/init-clients -+++ b/scripts/init-clients -@@ -49,7 +49,7 @@ if ! grep -q -s "$CHAIN_1_ID" "$CONFIG_FILE"; then - usage - fi - --GAIA_DATA="$(pwd)/data" -+GAIA_DATA="~/go/src/github.com/cosmos/relayer/data" - - CHAIN_0_RPC_PORT=26657 - CHAIN_0_RPC_ADDR="localhost:$CHAIN_0_RPC_PORT" -``` - -Now setup the clients for Hermes to use: - -```shell -$ ./scripts/init-clients ~/.hermes/config.toml ibc-0 ibc-1 - Building the Rust relayer... - Removing light client peers from configuration... - Adding primary peers to light client configuration... - Adding secondary peers to light client configuration... - Importing keys... - Done! -``` - -8. Test the `upgrade-client` CLI - -The following command will perform the upgrade for client `07-tendermint-0`. It -will output two events, one for the updated client state, and another for the -upgraded state. - -```shell -hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 -``` +1. Start two gaia instances and initialize hermes: + + ```shell + $ ./scripts/dev-env ~/.hermes/config.toml ibc-0 ibc-1 + ``` + The `one-chain` script is invoked for each chain and modifies the `genesis.json` file to use a short window for governance proposals (`200s` for `max_deposit_period` and `voting_period`). Therefore, an upgrade proposal can be submitted, voted on and accepted within a short time. + +2. Create one client on `ibc-1` for `ibc-0`: + + ```shell + $ hermes tx raw create-client ibc-1 ibc-0 + ``` + +3. Create and submit an upgrade plan for chain `ibc-0`: + + Use the hermes test command to make an upgrade proposal. In the example below a software upgrade proposal is made for `ibc-0`, for the height `300` blocks from latest height. `10000000stake` is deposited. + The proposal includes the upgraded client state constructed from the state of `07-tendermint-0` client on `ibc-1` that was created in the previous step. In addition, the `unbonding_period` of the client is set to some new value (`400h`) + + ```shell + $ hermes tx raw upgrade-chain ibc-0 ibc-1 07-tendermint-0 10000000 300 + ``` + + Note that the height offset should be picked such that the proposal plan height is reached after the `200s` voting period. + + 4. Verify that the proposal was accepted: + + Query the upgrade plan to check that it was submitted correctly. Note the `height` at which the proposal will take effect (chain halts). Also `status: PROPOSAL_STATUS_VOTING_PERIOD`. + + ```shell + $ gaiad query gov proposal 1 --home data/ibc-0/ + + content: + '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal + description: upgrade the chain software and unbonding period + plan: + height: "382" + info: upgrade the chain software and unbonding period + name: test + time: "0001-01-01T00:00:00Z" + upgraded_client_state: + '@type': /ibc.lightclients.tendermint.v1.ClientState + allow_update_after_expiry: false + allow_update_after_misbehaviour: false + chain_id: ibc-0 + frozen_height: + revision_height: "0" + revision_number: "0" + latest_height: + revision_height: "383" + revision_number: "0" + max_clock_drift: 0s + proof_specs: + ... + trust_level: + denominator: "0" + numerator: "0" + trusting_period: 0s + unbonding_period: 1440000s + upgrade_path: + - upgrade + - upgradedIBCState + title: upgrade_ibc_clients + deposit_end_time: "2021-03-23T17:25:42.543572Z" + final_tally_result: + abstain: "0" + "no": "0" + no_with_veto: "0" + "yes": "0" + proposal_id: "1" + status: PROPOSAL_STATUS_VOTING_PERIOD + submit_time: "2021-03-23T17:22:22.543572Z" + total_deposit: + - amount: "10000000" + denom: stake + voting_end_time: "2021-03-23T17:25:42.543572Z" + voting_start_time: "2021-03-23T17:22:22.543572Z" + ``` + + 5. Vote on the proposal + + The parameter `1` should match the `proposal_id:` from the upgrade proposal submitted at step 3. This command must be issued while the proposal status is `PROPOSAL_STATUS_VOTING_PERIOD`. + + ```shell + gaiad tx gov vote 1 yes --home data/ibc-0/data/ --keyring-backend test --keyring-dir data/ibc-0/ --chain-id ibc-0 --from validator + ``` + + Wait approximately 200 seconds until the proposal changes status to `PROPOSAL_STATUS_PASSED`. Note the `final tally_result` that includes the vote submitted in previous step. + + ```shell + $ gaiad query gov proposal 1 --home data/ibc-0/ + + content: + '@type': /cosmos.upgrade.v1beta1.SoftwareUpgradeProposal + description: upgrade the chain software and unbonding period + plan: + ... + final_tally_result: + abstain: "0" + "no": "0" + no_with_veto: "0" + "yes": "100000000000" + proposal_id: "1" + status: PROPOSAL_STATUS_PASSED + submit_time: "2021-03-23T17:22:22.543572Z" + total_deposit: + - amount: "10000000" + denom: stake + voting_end_time: "2021-03-23T17:25:42.543572Z" + voting_start_time: "2021-03-23T17:22:22.543572Z" + ``` + +6. Test the `upgrade-client` CLI + + The following command performs the upgrade for client `07-tendermint-0`. It outputs two events, one for the updated client state, and another for the upgraded state. + + ```shell + $ hermes tx raw upgrade-client ibc-1 ibc-0 07-tendermint-0 + + { + "status": "success", + "result": [ + { + "UpdateClient": { + "client_id": "07-tendermint-0", + "client_type": "Tendermint", + "consensus_height": { + "revision_height": 332, + "revision_number": 0 + }, + "height": { + "revision_height": 404, + "revision_number": 1 + } + } + }, + { + "UpgradeClient": { + "client_id": "07-tendermint-0", + "client_type": "Tendermint", + "consensus_height": { + "revision_height": 333, + "revision_number": 0 + }, + "height": { + "revision_height": 404, + "revision_number": 1 + } + } + } + ] + } + ``` diff --git a/modules/src/ics07_tendermint/client_state.rs b/modules/src/ics07_tendermint/client_state.rs index db77540163..88fcc9db4b 100644 --- a/modules/src/ics07_tendermint/client_state.rs +++ b/modules/src/ics07_tendermint/client_state.rs @@ -2,7 +2,9 @@ use std::convert::{TryFrom, TryInto}; use std::time::Duration; use serde::Serialize; -use tendermint::trust_threshold::TrustThresholdFraction as TrustThreshold; +use tendermint::trust_threshold::{ + TrustThresholdFraction as TrustThreshold, TrustThresholdFraction, +}; use tendermint_proto::Protobuf; use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawClientState, Fraction}; @@ -103,6 +105,22 @@ impl ClientState { ..self } } + + // Helper function to verify the upgrade client procedure, it resets all fields except the + // blockchain specific ones. + pub fn zero_custom_fields(&self) -> ClientState { + let mut client_state = self.clone(); + client_state.trusting_period = Duration::default(); + client_state.trust_level = TrustThresholdFraction { + numerator: 0, + denominator: 0, + }; + client_state.allow_update_after_expiry = false; + client_state.allow_update_after_misbehaviour = false; + client_state.frozen_height = Height::zero(); + client_state.max_clock_drift = Duration::default(); + client_state + } } impl crate::ics02_client::client_state::ClientState for ClientState { diff --git a/proto-compiler/src/cmd/compile.rs b/proto-compiler/src/cmd/compile.rs index 145cd67a90..733ee8b5ee 100644 --- a/proto-compiler/src/cmd/compile.rs +++ b/proto-compiler/src/cmd/compile.rs @@ -122,6 +122,7 @@ impl CompileCmd { let proto_paths = [ format!("{}/../proto/definitions/mock", root), format!("{}/proto/cosmos/auth", sdk_dir.display()), + format!("{}/proto/cosmos/gov", sdk_dir.display()), format!("{}/proto/cosmos/tx", sdk_dir.display()), format!("{}/proto/cosmos/base", sdk_dir.display()), format!("{}/proto/cosmos/staking", sdk_dir.display()), diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 15b2a20bdb..43a8f37742 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -75,6 +75,11 @@ pub mod cosmos { include!("prost/cosmos.upgrade.v1beta1.rs"); } } + pub mod gov { + pub mod v1beta1 { + include!("prost/cosmos.gov.v1beta1.rs"); + } + } } pub mod ibc { diff --git a/proto/src/prost/cosmos.gov.v1beta1.rs b/proto/src/prost/cosmos.gov.v1beta1.rs new file mode 100644 index 0000000000..6bf3e94f9a --- /dev/null +++ b/proto/src/prost/cosmos.gov.v1beta1.rs @@ -0,0 +1,352 @@ +/// TextProposal defines a standard text proposal whose changes need to be +/// manually updated in case of approval. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TextProposal { + #[prost(string, tag="1")] + pub title: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub description: ::prost::alloc::string::String, +} +/// Deposit defines an amount deposited by an account address to an active +/// proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Deposit { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, + #[prost(message, repeated, tag="3")] + pub amount: ::prost::alloc::vec::Vec, +} +/// Proposal defines the core field members of a governance proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Proposal { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(message, optional, tag="2")] + pub content: ::core::option::Option<::prost_types::Any>, + #[prost(enumeration="ProposalStatus", tag="3")] + pub status: i32, + #[prost(message, optional, tag="4")] + pub final_tally_result: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub submit_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, optional, tag="6")] + pub deposit_end_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, repeated, tag="7")] + pub total_deposit: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag="8")] + pub voting_start_time: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, optional, tag="9")] + pub voting_end_time: ::core::option::Option<::prost_types::Timestamp>, +} +/// TallyResult defines a standard tally for a governance proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TallyResult { + #[prost(string, tag="1")] + pub yes: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub abstain: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub no: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub no_with_veto: ::prost::alloc::string::String, +} +/// Vote defines a vote on a governance proposal. +/// A Vote consists of a proposal ID, the voter, and the vote option. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Vote { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + #[prost(enumeration="VoteOption", tag="3")] + pub option: i32, +} +/// DepositParams defines the params for deposits on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositParams { + /// Minimum deposit for a proposal to enter voting period. + #[prost(message, repeated, tag="1")] + pub min_deposit: ::prost::alloc::vec::Vec, + /// Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + /// months. + #[prost(message, optional, tag="2")] + pub max_deposit_period: ::core::option::Option<::prost_types::Duration>, +} +/// VotingParams defines the params for voting on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VotingParams { + /// Length of the voting period. + #[prost(message, optional, tag="1")] + pub voting_period: ::core::option::Option<::prost_types::Duration>, +} +/// TallyParams defines the params for tallying votes on governance proposals. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TallyParams { + /// Minimum percentage of total stake needed to vote for a result to be + /// considered valid. + #[prost(bytes="vec", tag="1")] + pub quorum: ::prost::alloc::vec::Vec, + /// Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + #[prost(bytes="vec", tag="2")] + pub threshold: ::prost::alloc::vec::Vec, + /// Minimum value of Veto votes to Total votes ratio for proposal to be + /// vetoed. Default value: 1/3. + #[prost(bytes="vec", tag="3")] + pub veto_threshold: ::prost::alloc::vec::Vec, +} +/// VoteOption enumerates the valid vote options for a given governance proposal. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum VoteOption { + /// VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + Unspecified = 0, + /// VOTE_OPTION_YES defines a yes vote option. + Yes = 1, + /// VOTE_OPTION_ABSTAIN defines an abstain vote option. + Abstain = 2, + /// VOTE_OPTION_NO defines a no vote option. + No = 3, + /// VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + NoWithVeto = 4, +} +/// ProposalStatus enumerates the valid statuses of a proposal. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ProposalStatus { + /// PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + Unspecified = 0, + /// PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + /// period. + DepositPeriod = 1, + /// PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + /// period. + VotingPeriod = 2, + /// PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + /// passed. + Passed = 3, + /// PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + /// been rejected. + Rejected = 4, + /// PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + /// failed. + Failed = 5, +} +/// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +/// proposal Content. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgSubmitProposal { + #[prost(message, optional, tag="1")] + pub content: ::core::option::Option<::prost_types::Any>, + #[prost(message, repeated, tag="2")] + pub initial_deposit: ::prost::alloc::vec::Vec, + #[prost(string, tag="3")] + pub proposer: ::prost::alloc::string::String, +} +/// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgSubmitProposalResponse { + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// MsgVote defines a message to cast a vote. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgVote { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + #[prost(enumeration="VoteOption", tag="3")] + pub option: i32, +} +/// MsgVoteResponse defines the Msg/Vote response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgVoteResponse { +} +/// MsgDeposit defines a message to submit a deposit to an existing proposal. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgDeposit { + #[prost(uint64, tag="1")] + pub proposal_id: u64, + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, + #[prost(message, repeated, tag="3")] + pub amount: ::prost::alloc::vec::Vec, +} +/// MsgDepositResponse defines the Msg/Deposit response type. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgDepositResponse { +} +# [doc = r" Generated client implementations."] pub mod msg_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Msg defines the bank Msg service."] pub struct MsgClient < T > { inner : tonic :: client :: Grpc < T > , } impl MsgClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > MsgClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " SubmitProposal defines a method to create new proposal given a content."] pub async fn submit_proposal (& mut self , request : impl tonic :: IntoRequest < super :: MsgSubmitProposal > ,) -> Result < tonic :: Response < super :: MsgSubmitProposalResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/SubmitProposal") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Vote defines a method to add a vote on a specific proposal."] pub async fn vote (& mut self , request : impl tonic :: IntoRequest < super :: MsgVote > ,) -> Result < tonic :: Response < super :: MsgVoteResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/Vote") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposit defines a method to add deposit on a specific proposal."] pub async fn deposit (& mut self , request : impl tonic :: IntoRequest < super :: MsgDeposit > ,) -> Result < tonic :: Response < super :: MsgDepositResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Msg/Deposit") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for MsgClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for MsgClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "MsgClient {{ ... }}") } } }/// QueryProposalRequest is the request type for the Query/Proposal RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// QueryProposalResponse is the response type for the Query/Proposal RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalResponse { + #[prost(message, optional, tag="1")] + pub proposal: ::core::option::Option, +} +/// QueryProposalsRequest is the request type for the Query/Proposals RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalsRequest { + /// proposal_status defines the status of the proposals. + #[prost(enumeration="ProposalStatus", tag="1")] + pub proposal_status: i32, + /// voter defines the voter address for the proposals. + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, + /// depositor defines the deposit addresses from the proposals. + #[prost(string, tag="3")] + pub depositor: ::prost::alloc::string::String, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="4")] + pub pagination: ::core::option::Option, +} +/// QueryProposalsResponse is the response type for the Query/Proposals RPC +/// method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryProposalsResponse { + #[prost(message, repeated, tag="1")] + pub proposals: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryVoteRequest is the request type for the Query/Vote RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVoteRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// voter defines the oter address for the proposals. + #[prost(string, tag="2")] + pub voter: ::prost::alloc::string::String, +} +/// QueryVoteResponse is the response type for the Query/Vote RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVoteResponse { + /// vote defined the queried vote. + #[prost(message, optional, tag="1")] + pub vote: ::core::option::Option, +} +/// QueryVotesRequest is the request type for the Query/Votes RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVotesRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryVotesResponse is the response type for the Query/Votes RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryVotesResponse { + /// votes defined the queried votes. + #[prost(message, repeated, tag="1")] + pub votes: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryParamsRequest is the request type for the Query/Params RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryParamsRequest { + /// params_type defines which parameters to query for, can be one of "voting", + /// "tallying" or "deposit". + #[prost(string, tag="1")] + pub params_type: ::prost::alloc::string::String, +} +/// QueryParamsResponse is the response type for the Query/Params RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryParamsResponse { + /// voting_params defines the parameters related to voting. + #[prost(message, optional, tag="1")] + pub voting_params: ::core::option::Option, + /// deposit_params defines the parameters related to deposit. + #[prost(message, optional, tag="2")] + pub deposit_params: ::core::option::Option, + /// tally_params defines the parameters related to tally. + #[prost(message, optional, tag="3")] + pub tally_params: ::core::option::Option, +} +/// QueryDepositRequest is the request type for the Query/Deposit RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// depositor defines the deposit addresses from the proposals. + #[prost(string, tag="2")] + pub depositor: ::prost::alloc::string::String, +} +/// QueryDepositResponse is the response type for the Query/Deposit RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositResponse { + /// deposit defines the requested deposit. + #[prost(message, optional, tag="1")] + pub deposit: ::core::option::Option, +} +/// QueryDepositsRequest is the request type for the Query/Deposits RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositsRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, + /// pagination defines an optional pagination for the request. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryDepositsResponse is the response type for the Query/Deposits RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryDepositsResponse { + #[prost(message, repeated, tag="1")] + pub deposits: ::prost::alloc::vec::Vec, + /// pagination defines the pagination in the response. + #[prost(message, optional, tag="2")] + pub pagination: ::core::option::Option, +} +/// QueryTallyResultRequest is the request type for the Query/Tally RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryTallyResultRequest { + /// proposal_id defines the unique id of the proposal. + #[prost(uint64, tag="1")] + pub proposal_id: u64, +} +/// QueryTallyResultResponse is the response type for the Query/Tally RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryTallyResultResponse { + /// tally defines the requested tally. + #[prost(message, optional, tag="1")] + pub tally: ::core::option::Option, +} +# [doc = r" Generated client implementations."] pub mod query_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Query defines the gRPC querier service for gov module"] pub struct QueryClient < T > { inner : tonic :: client :: Grpc < T > , } impl QueryClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > QueryClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " Proposal queries proposal details based on ProposalID."] pub async fn proposal (& mut self , request : impl tonic :: IntoRequest < super :: QueryProposalRequest > ,) -> Result < tonic :: Response < super :: QueryProposalResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Proposal") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Proposals queries all proposals based on given status."] pub async fn proposals (& mut self , request : impl tonic :: IntoRequest < super :: QueryProposalsRequest > ,) -> Result < tonic :: Response < super :: QueryProposalsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Proposals") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Vote queries voted information based on proposalID, voterAddr."] pub async fn vote (& mut self , request : impl tonic :: IntoRequest < super :: QueryVoteRequest > ,) -> Result < tonic :: Response < super :: QueryVoteResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Vote") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Votes queries votes of a given proposal."] pub async fn votes (& mut self , request : impl tonic :: IntoRequest < super :: QueryVotesRequest > ,) -> Result < tonic :: Response < super :: QueryVotesResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Votes") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Params queries all parameters of the gov module."] pub async fn params (& mut self , request : impl tonic :: IntoRequest < super :: QueryParamsRequest > ,) -> Result < tonic :: Response < super :: QueryParamsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Params") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposit queries single deposit information based proposalID, depositAddr."] pub async fn deposit (& mut self , request : impl tonic :: IntoRequest < super :: QueryDepositRequest > ,) -> Result < tonic :: Response < super :: QueryDepositResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Deposit") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Deposits queries all deposits of a single proposal."] pub async fn deposits (& mut self , request : impl tonic :: IntoRequest < super :: QueryDepositsRequest > ,) -> Result < tonic :: Response < super :: QueryDepositsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/Deposits") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " TallyResult queries the tally of a proposal vote."] pub async fn tally_result (& mut self , request : impl tonic :: IntoRequest < super :: QueryTallyResultRequest > ,) -> Result < tonic :: Response < super :: QueryTallyResultResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.gov.v1beta1.Query/TallyResult") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for QueryClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for QueryClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "QueryClient {{ ... }}") } } }/// GenesisState defines the gov module's genesis state. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenesisState { + /// starting_proposal_id is the ID of the starting proposal. + #[prost(uint64, tag="1")] + pub starting_proposal_id: u64, + /// deposits defines all the deposits present at genesis. + #[prost(message, repeated, tag="2")] + pub deposits: ::prost::alloc::vec::Vec, + /// votes defines all the votes present at genesis. + #[prost(message, repeated, tag="3")] + pub votes: ::prost::alloc::vec::Vec, + /// proposals defines all the proposals present at genesis. + #[prost(message, repeated, tag="4")] + pub proposals: ::prost::alloc::vec::Vec, + /// params defines all the paramaters of related to deposit. + #[prost(message, optional, tag="5")] + pub deposit_params: ::core::option::Option, + /// params defines all the paramaters of related to voting. + #[prost(message, optional, tag="6")] + pub voting_params: ::core::option::Option, + /// params defines all the paramaters of related to tally. + #[prost(message, optional, tag="7")] + pub tally_params: ::core::option::Option, +} diff --git a/relayer-cli/src/commands/tx.rs b/relayer-cli/src/commands/tx.rs index cb64a1c63e..b0008a7fe0 100644 --- a/relayer-cli/src/commands/tx.rs +++ b/relayer-cli/src/commands/tx.rs @@ -8,6 +8,7 @@ mod client; mod connection; mod packet; mod transfer; +mod upgrade; /// `tx` subcommand #[derive(Command, Debug, Options, Runnable)] @@ -90,4 +91,8 @@ pub enum TxRawCommands { /// The `tx raw packet-ack` subcommand #[options(help = "Relay acknowledgment packets")] PacketAck(packet::TxRawPacketAckCmd), + + /// The `tx raw upgrade-chain` subcommand + #[options(help = "Send an upgrade plan")] + UpgradeChain(upgrade::TxUpgradeChainCmd), } diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index a405d8bf7a..5f8a6363a8 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -120,7 +120,7 @@ pub struct TxUpgradeClientCmd { required, help = "identifier of the client to be upgraded on destination chain" )] - client_id: ClientId, + dst_client_id: ClientId, } impl Runnable for TxUpgradeClientCmd { @@ -140,7 +140,7 @@ impl Runnable for TxUpgradeClientCmd { // Instantiate the client hosted on the destination chain, which is targeting headers for // the source chain. - let client = ForeignClient::find(chains.src, chains.dst, &self.client_id) + let client = ForeignClient::find(chains.src, chains.dst, &self.dst_client_id) .unwrap_or_else(exit_with_unrecoverable_error); let outcome = client.upgrade(); diff --git a/relayer-cli/src/commands/tx/upgrade.rs b/relayer-cli/src/commands/tx/upgrade.rs new file mode 100644 index 0000000000..7f2d890677 --- /dev/null +++ b/relayer-cli/src/commands/tx/upgrade.rs @@ -0,0 +1,101 @@ +use std::sync::Arc; + +use abscissa_core::{Command, Options, Runnable}; +use tokio::runtime::Runtime as TokioRuntime; + +use ibc::events::IbcEvent; +use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc_relayer::upgrade_chain::{build_and_send_upgrade_chain_message, UpdatePlanOptions}; +use ibc_relayer::{ + chain::{Chain, CosmosSdkChain}, + config::Config, +}; + +use crate::conclude::Output; +use crate::error::{Error, Kind}; +use crate::prelude::*; + +#[derive(Clone, Command, Debug, Options)] +pub struct TxUpgradeChainCmd { + #[options(free, required, help = "identifier of the chain to upgrade")] + dst_chain_id: ChainId, + + #[options(free, required, help = "identifier of the source chain")] + src_chain_id: ChainId, + + #[options( + free, + required, + help = "identifier of the client on source chain from which the plan is created" + )] + src_client_id: ClientId, + + #[options(free, required, help = "amount of stake")] + amount: u64, + + #[options( + free, + required, + help = "upgrade height offset in number of blocks since current" + )] + height_offset: u64, +} + +impl TxUpgradeChainCmd { + fn validate_options(&self, config: &Config) -> Result { + let src_chain_config = config + .find_chain(&self.src_chain_id) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let dst_chain_config = config + .find_chain(&self.dst_chain_id) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let opts = UpdatePlanOptions { + dst_chain_config: dst_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + src_client_id: self.src_client_id.clone(), + amount: self.amount, + height_offset: self.height_offset, + }; + + Ok(opts) + } +} + +impl Runnable for TxUpgradeChainCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => return Output::error(err).exit(), + Ok(result) => result, + }; + info!("Message {:?}", opts); + + let rt = Arc::new(TokioRuntime::new().unwrap()); + + let src_chain_res = CosmosSdkChain::bootstrap(opts.src_chain_config.clone(), rt.clone()) + .map_err(|e| Kind::Runtime.context(e)); + let src_chain = match src_chain_res { + Ok(chain) => chain, + Err(e) => return Output::error(format!("{}", e)).exit(), + }; + + let dst_chain_res = CosmosSdkChain::bootstrap(opts.dst_chain_config.clone(), rt) + .map_err(|e| Kind::Runtime.context(e)); + let dst_chain = match dst_chain_res { + Ok(chain) => chain, + Err(e) => return Output::error(format!("{}", e)).exit(), + }; + + let res: Result, Error> = + build_and_send_upgrade_chain_message(dst_chain, src_chain, &opts) + .map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(ev) => Output::success(ev).exit(), + Err(e) => Output::error(format!("{}", e)).exit(), + } + } +} diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index 472f9496fd..e14841c1fb 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -27,4 +27,5 @@ pub mod macros; pub mod relay; pub mod supervisor; pub mod transfer; +pub mod upgrade_chain; pub mod util; diff --git a/relayer/src/upgrade_chain.rs b/relayer/src/upgrade_chain.rs new file mode 100644 index 0000000000..963ba26444 --- /dev/null +++ b/relayer/src/upgrade_chain.rs @@ -0,0 +1,124 @@ +use bitcoin::hashes::core::time::Duration; +use prost_types::Any; +use thiserror::Error; +use tracing::error; + +use ibc::events::IbcEvent; +use ibc::ics02_client::client_state::AnyClientState; +use ibc::ics02_client::height::Height; +use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc_proto::cosmos::gov::v1beta1::MsgSubmitProposal; +use ibc_proto::cosmos::upgrade::v1beta1::{Plan, SoftwareUpgradeProposal}; + +use crate::chain::{Chain, CosmosSdkChain}; +use crate::config::ChainConfig; +use crate::error::Error; + +#[derive(Debug, Error)] +pub enum UpgradeChainError { + #[error("failed with underlying cause: {0}")] + Failed(String), + + #[error("key error with underlying cause: {0}")] + KeyError(Error), + + #[error( + "failed during a transaction submission step to chain id {0} with underlying error: {1}" + )] + SubmitError(ChainId, Error), +} + +#[derive(Clone, Debug)] +pub struct UpdatePlanOptions { + pub src_chain_config: ChainConfig, + pub dst_chain_config: ChainConfig, + pub src_client_id: ClientId, + pub amount: u64, + pub height_offset: u64, +} + +pub fn build_and_send_upgrade_chain_message( + mut dst_chain: CosmosSdkChain, // the chain whose account is debited + src_chain: CosmosSdkChain, // the chain where the transfer is sent + opts: &UpdatePlanOptions, +) -> Result, UpgradeChainError> { + // build a proposal Plan + let upgrade_height = dst_chain + .query_latest_height() + .unwrap() + .add(opts.height_offset); + let client_state = src_chain + .query_client_state(&opts.src_client_id, Height::zero()) + .unwrap(); + let mut upgraded_client_state = client_state.zero_custom_fields(); + upgraded_client_state.latest_height = upgrade_height.increment(); + upgraded_client_state.unbonding_period = Duration::from_secs(400 * 3600); // TODO add to options + + let raw_client_state = AnyClientState::Tendermint(upgraded_client_state); + let plan = Plan { + name: "test".to_string(), + time: None, + height: upgrade_height.revision_height as i64, + info: "upgrade the chain software and unbonding period".to_string(), + upgraded_client_state: Some(Any::from(raw_client_state)), + }; + + // build the proposal + let proposal = SoftwareUpgradeProposal { + title: "upgrade_ibc_clients".to_string(), + description: "upgrade the chain software and unbonding period".to_string(), + plan: Some(plan), + }; + let mut buf_proposal = Vec::new(); + prost::Message::encode(&proposal, &mut buf_proposal).unwrap(); + let any_proposal = Any { + type_url: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal".to_string(), + value: buf_proposal, + }; + + // build the msg submit proposal + let proposer = dst_chain + .get_signer() + .map_err(UpgradeChainError::KeyError)?; + + let coins = ibc_proto::cosmos::base::v1beta1::Coin { + denom: "stake".to_string(), + amount: opts.amount.to_string(), + }; + + let msg = MsgSubmitProposal { + content: Some(any_proposal), + initial_deposit: vec![coins], + proposer: proposer.to_string(), + }; + + let mut buf_msg = Vec::new(); + prost::Message::encode(&msg, &mut buf_msg).unwrap(); + let any_msg = Any { + type_url: "/cosmos.gov.v1beta1.MsgSubmitProposal".to_string(), + value: buf_msg, + }; + + let events = dst_chain + .send_msgs(vec![any_msg]) + .map_err(|e| UpgradeChainError::SubmitError(dst_chain.id().clone(), e))?; + + // Check if the chain rejected the transaction + let result = events + .iter() + .find(|event| matches!(event, IbcEvent::ChainError(_))); + + match result { + None => Ok(events), + Some(err) => { + if let IbcEvent::ChainError(err) = err { + Err(UpgradeChainError::Failed(err.to_string())) + } else { + panic!( + "internal error, expected IBCEvent::ChainError, got {:?}", + err + ) + } + } + } +} diff --git a/scripts/one-chain b/scripts/one-chain index ece3958310..e7e0fed600 100755 --- a/scripts/one-chain +++ b/scripts/one-chain @@ -105,6 +105,7 @@ fi # Set proper defaults and change ports (use a different sed for Mac or Linux) echo "Change settings in config.toml file..." if [ $platform = 'linux' ]; then + sed -i 's#"172800s"#"200s"#g' $CHAIN_DIR/$CHAIN_ID/config/genesis.json sed -i 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPC_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2P_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i 's#"localhost:6060"#"localhost:'"$PROF_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml @@ -113,6 +114,7 @@ if [ $platform = 'linux' ]; then sed -i 's/index_all_keys = false/index_all_keys = true/g' $CHAIN_DIR/$CHAIN_ID/config/config.toml # sed -i '' 's#index-events = \[\]#index-events = \["message.action","send_packet.packet_src_channel","send_packet.packet_sequence"\]#g' $CHAIN_DIR/$CHAIN_ID/config/app.toml else + sed -i '' 's#"172800s"#"200s"#g' $CHAIN_DIR/$CHAIN_ID/config/genesis.json sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPC_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i '' 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2P_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml sed -i '' 's#"localhost:6060"#"localhost:'"$PROF_PORT"'"#g' $CHAIN_DIR/$CHAIN_ID/config/config.toml