From bc58a99bd8d6d9946de008c7da50b9a63211fcb4 Mon Sep 17 00:00:00 2001 From: srdtrk <59252793+srdtrk@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:46:10 +0300 Subject: [PATCH] style!: enforcing clippy::pedantic and clippy::nursery (#24) * style: comply with clippy::pedantic * style!: added clippy::nursery and deny warnings * imp(e2e): bumped wasmd to 0.45.0 * ci: trying to add dependency caching * deps: bump crate version to 0.3.0 * deps: Cargo.lock updated * imp: fixed test * fix(testing/owner): fix linter issues after breaking api changes * style: ran 'cargo fmt' * fix: fixed incorrect reversion --- .github/workflows/e2e.yml | 3 + Cargo.lock | 2 +- Cargo.toml | 2 +- e2e/interchaintest/chain_config.go | 2 +- src/contract.rs | 13 +++- src/helpers.rs | 77 +++++++++++++------ src/ibc/handshake.rs | 3 + src/ibc/relay.rs | 5 +- src/ibc/types/events.rs | 6 +- src/ibc/types/keys.rs | 4 +- src/ibc/types/metadata.rs | 46 +++++++---- src/ibc/types/packet.rs | 44 ++++++----- src/ibc/types/stargate.rs | 35 +++++---- src/lib.rs | 1 + src/types/callbacks.rs | 16 +++- src/types/cosmos_msg.rs | 6 +- src/types/error.rs | 5 +- src/types/keys.rs | 4 +- src/types/mod.rs | 1 + src/types/msg.rs | 6 +- src/types/state.rs | 77 +++++++++++-------- testing/contracts/cw-ica-owner/Cargo.lock | 2 +- .../contracts/cw-ica-owner/src/contract.rs | 2 +- 23 files changed, 232 insertions(+), 130 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 4cbc3f37..378b5e2a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,6 +13,9 @@ jobs: - uses: actions/setup-go@v4 with: go-version: "1.20" + check-latest: true + cache-dependency-path: | + e2e/interchaintest/go.sum - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3.6.0 diff --git a/Cargo.lock b/Cargo.lock index 8d43e48c..00c07160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,7 +232,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.2.0" +version = "0.3.0" dependencies = [ "base64 0.13.1", "cosmos-sdk-proto", diff --git a/Cargo.toml b/Cargo.toml index 745e0e26..2d073a07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-ica-controller" -version = "0.2.0" +version = "0.3.0" authors = ["srdtrk "] edition = "2021" description = "This is a cosmwasm implementation of an interchain accounts controller." diff --git a/e2e/interchaintest/chain_config.go b/e2e/interchaintest/chain_config.go index ea910b33..f8b72cd1 100644 --- a/e2e/interchaintest/chain_config.go +++ b/e2e/interchaintest/chain_config.go @@ -16,7 +16,7 @@ var chainSpecs = []*interchaintest.ChainSpec{ Images: []ibc.DockerImage{ { Repository: "cosmwasm/wasmd", // FOR LOCAL IMAGE USE: Docker Image Name - Version: "v0.41.0", // FOR LOCAL IMAGE USE: Docker Image Tag + Version: "v0.45.0", // FOR LOCAL IMAGE USE: Docker Image Tag }, }, Bin: "wasmd", diff --git a/src/contract.rs b/src/contract.rs index 2cdebad7..92c555ec 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1,5 +1,8 @@ //! This module handles the execution logic of the contract. +// Clippy pedantic is disabled for `entry_point` functions since they require a certain signature. +#![allow(clippy::pedantic)] + use cosmwasm_std::entry_point; use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; @@ -308,8 +311,7 @@ mod tests { }; let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - let expected_packet = - IcaPacketData::from_json_strings(vec![custom_msg_str.to_string()], None); + let expected_packet = IcaPacketData::from_json_strings(&[custom_msg_str.to_string()], None); let expected_msg = expected_packet.to_ibc_msg(&env, "channel-0", None).unwrap(); assert_eq!(1, res.messages.len()); @@ -426,7 +428,7 @@ mod tests { let _res = instantiate( deps.as_mut(), mock_env(), - info.clone(), + info, InstantiateMsg { admin: None, channel_open_init_options: None, @@ -456,7 +458,10 @@ mod tests { let res = migrate(deps.as_mut(), mock_env(), MigrateMsg {}); assert_eq!( res.unwrap_err().to_string(), - "invalid migration version: expected > 100.0.0, got 0.2.0".to_string() + format!( + "invalid migration version: expected > 100.0.0, got {}", + CONTRACT_VERSION + ) ); } } diff --git a/src/helpers.rs b/src/helpers.rs index a83e25c3..076b5f8e 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -8,28 +8,34 @@ use cosmwasm_std::{to_json_binary, Addr, Binary, CosmosMsg, QuerierWrapper, StdR use crate::types::{msg, state}; -/// CwIcaControllerContract is a wrapper around Addr that provides helpers +/// `CwIcaControllerContract` is a wrapper around Addr that provides helpers /// for working with this contract. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct CwIcaControllerContract(pub Addr); -/// CwIcaControllerCodeId is a wrapper around u64 that provides helpers for +/// `CwIcaControllerCodeId` is a wrapper around u64 that provides helpers for /// initializing this contract. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct CwIcaControllerCode(pub u64); impl CwIcaControllerContract { /// new creates a new [`CwIcaControllerContract`] - pub fn new(addr: Addr) -> Self { + #[must_use] + pub const fn new(addr: Addr) -> Self { Self(addr) } /// addr returns the address of the contract + #[must_use] pub fn addr(&self) -> Addr { self.0.clone() } /// call creates a [`WasmMsg::Execute`] message targeting this contract, + /// + /// # Errors + /// + /// This function returns an error if the given message cannot be serialized pub fn call(&self, msg: impl Into) -> StdResult { let msg = to_json_binary(&msg.into())?; Ok(WasmMsg::Execute { @@ -40,17 +46,29 @@ impl CwIcaControllerContract { .into()) } - /// query_channel queries the [`state::ChannelState`] of this contract + /// `query_channel` queries the [`state::ChannelState`] of this contract + /// + /// # Errors + /// + /// This function returns an error if the query fails pub fn query_channel(&self, querier: QuerierWrapper) -> StdResult { querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetChannel {}) } - /// query_state queries the [`state::ContractState`] of this contract + /// `query_state` queries the [`state::ContractState`] of this contract + /// + /// # Errors + /// + /// This function returns an error if the query fails pub fn query_state(&self, querier: QuerierWrapper) -> StdResult { querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetContractState {}) } - /// query_callback_counter queries the [`state::CallbackCounter`] of this contract + /// `query_callback_counter` queries the [`state::CallbackCounter`] of this contract + /// + /// # Errors + /// + /// This function returns an error if the query fails pub fn query_callback_counter( &self, querier: QuerierWrapper, @@ -58,24 +76,29 @@ impl CwIcaControllerContract { querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetCallbackCounter {}) } - /// update_admin creates a [`WasmMsg::UpdateAdmin`] message targeting this contract - pub fn update_admin(&self, admin: impl Into) -> StdResult { - Ok(WasmMsg::UpdateAdmin { + /// `update_admin` creates a [`WasmMsg::UpdateAdmin`] message targeting this contract + pub fn update_admin(&self, admin: impl Into) -> CosmosMsg { + WasmMsg::UpdateAdmin { contract_addr: self.addr().into(), admin: admin.into(), } - .into()) + .into() } - /// clear_admin creates a [`WasmMsg::ClearAdmin`] message targeting this contract - pub fn clear_admin(&self) -> StdResult { - Ok(WasmMsg::ClearAdmin { + /// `clear_admin` creates a [`WasmMsg::ClearAdmin`] message targeting this contract + #[must_use] + pub fn clear_admin(&self) -> CosmosMsg { + WasmMsg::ClearAdmin { contract_addr: self.addr().into(), } - .into()) + .into() } - /// migrate creates a [`WasmMsg::Migrate`] message targeting this contract + /// `migrate` creates a [`WasmMsg::Migrate`] message targeting this contract + /// + /// # Errors + /// + /// This function returns an error if the given message cannot be serialized pub fn migrate( &self, msg: impl Into, @@ -93,16 +116,22 @@ impl CwIcaControllerContract { impl CwIcaControllerCode { /// new creates a new [`CwIcaControllerCode`] - pub fn new(code_id: u64) -> Self { + #[must_use] + pub const fn new(code_id: u64) -> Self { Self(code_id) } - /// code_id returns the code id of this code - pub fn code_id(&self) -> u64 { + /// `code_id` returns the code id of this code + #[must_use] + pub const fn code_id(&self) -> u64 { self.0 } - /// instantiate creates a [`WasmMsg::Instantiate`] message targeting this code + /// `instantiate` creates a [`WasmMsg::Instantiate`] message targeting this code + /// + /// # Errors + /// + /// This function returns an error if the given message cannot be serialized pub fn instantiate( &self, msg: impl Into, @@ -115,12 +144,16 @@ impl CwIcaControllerCode { msg, funds: vec![], label: label.into(), - admin: admin.map(|s| s.into()), + admin: admin.map(Into::into), } .into()) } - /// instantiate2 creates a [`WasmMsg::Instantiate2`] message targeting this code + /// `instantiate2` creates a [`WasmMsg::Instantiate2`] message targeting this code + /// + /// # Errors + /// + /// This function returns an error if the given message cannot be serialized pub fn instantiate2( &self, msg: impl Into, @@ -134,7 +167,7 @@ impl CwIcaControllerCode { msg, funds: vec![], label: label.into(), - admin: admin.map(|s| s.into()), + admin: admin.map(Into::into), salt: salt.into(), } .into()) diff --git a/src/ibc/handshake.rs b/src/ibc/handshake.rs index ad73d6ba..1ec60a47 100644 --- a/src/ibc/handshake.rs +++ b/src/ibc/handshake.rs @@ -1,5 +1,8 @@ //! This module contains the entry points for the IBC handshake. +// Clippy pedantic is disabled for `entry_point` functions since they require a certain signature. +#![allow(clippy::pedantic)] + use cosmwasm_std::entry_point; use cosmwasm_std::{ DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannel, IbcChannelCloseMsg, diff --git a/src/ibc/relay.rs b/src/ibc/relay.rs index 9ff0220a..dccf5bc7 100644 --- a/src/ibc/relay.rs +++ b/src/ibc/relay.rs @@ -3,6 +3,9 @@ //! - The IBC packet timeout. //! - The IBC packet receive. +// Clippy pedantic is disabled for `entry_point` functions since they require a certain signature. +#![allow(clippy::pedantic)] + use cosmwasm_std::entry_point; use cosmwasm_std::{ from_json, DepsMut, Env, IbcBasicResponse, IbcPacketAckMsg, IbcPacketReceiveMsg, @@ -14,7 +17,7 @@ use crate::types::{ ContractError, }; -use super::types::{events, packet::acknowledgement::AcknowledgementData}; +use super::types::{events, packet::acknowledgement::Data as AcknowledgementData}; /// Implements the IBC module's `OnAcknowledgementPacket` handler. #[entry_point] diff --git a/src/ibc/types/events.rs b/src/ibc/types/events.rs index 25e802a9..eb5b5ab9 100644 --- a/src/ibc/types/events.rs +++ b/src/ibc/types/events.rs @@ -20,11 +20,12 @@ use cosmwasm_std::{Event, IbcPacket}; pub mod packet_ack { use cosmwasm_std::Binary; - use super::*; + use super::{attributes, Event, IbcPacket}; const EVENT_TYPE: &str = "acknowledge_packet"; /// returns an event for a successful packet acknowledgement. + #[must_use] pub fn success(packet: &IbcPacket, resp: &Binary) -> Event { Event::new(EVENT_TYPE) .add_attributes(attributes::from_packet(packet)) @@ -32,6 +33,7 @@ pub mod packet_ack { } /// returns an event for an unsuccessful packet acknowledgement. + #[must_use] pub fn error(packet: &IbcPacket, err: &str) -> Event { Event::new(EVENT_TYPE) .add_attributes(attributes::from_packet(packet)) @@ -40,7 +42,7 @@ pub mod packet_ack { } mod attributes { - use super::*; + use super::IbcPacket; use cosmwasm_std::Attribute; pub const ACK_BASE64: &str = "packet_ack_base64"; diff --git a/src/ibc/types/keys.rs b/src/ibc/types/keys.rs index 7e28d552..fecca730 100644 --- a/src/ibc/types/keys.rs +++ b/src/ibc/types/keys.rs @@ -1,7 +1,7 @@ //! Contains the key constants' definitions for the ibc module. -/// Version defines the current version for interchain accounts module +/// `ICA_VERSION` defines the current version for interchain accounts module pub const ICA_VERSION: &str = "ics27-1"; -/// HOST_PORT_ID is the default port id that the interchain accounts host submodule binds to +/// `HOST_PORT_ID` is the default port id that the interchain accounts host submodule binds to pub const HOST_PORT_ID: &str = "icahost"; diff --git a/src/ibc/types/metadata.rs b/src/ibc/types/metadata.rs index bd6b132e..aed7c6f6 100644 --- a/src/ibc/types/metadata.rs +++ b/src/ibc/types/metadata.rs @@ -13,7 +13,8 @@ use crate::types::{state::CHANNEL_STATE, ContractError}; use super::keys::ICA_VERSION; -/// IcaMetadata is the metadata of the IBC application communicated during the handshake. +/// `IcaMetadata` is the metadata of the IBC application communicated during the handshake. +#[allow(clippy::module_name_repetitions)] #[cw_serde] pub struct IcaMetadata { /// The version of the IBC application. @@ -34,20 +35,21 @@ pub struct IcaMetadata { pub tx_type: String, } -/// Encoding is the encoding of the transactions sent to the ICA host. +/// `TxEncoding` is the encoding of the transactions sent to the ICA host. #[cw_serde] pub enum TxEncoding { - /// Protobuf is the protobuf serialization of the CosmosSDK's Any. + /// `Protobuf` is the protobuf serialization of the CosmosSDK's Any. #[serde(rename = "proto3")] Protobuf, - /// Proto3Json is the json serialization of the CosmosSDK's Any. + /// `Proto3Json` is the json serialization of the CosmosSDK's Any. #[serde(rename = "proto3json")] Proto3Json, } impl IcaMetadata { - /// Creates a new IcaMetadata - pub fn new( + /// Creates a new [`IcaMetadata`] + #[must_use] + pub const fn new( version: String, controller_connection_id: String, host_connection_id: String, @@ -65,13 +67,14 @@ impl IcaMetadata { } } - /// Creates a new IcaMetadata from an IbcChannel + /// Creates a new [`IcaMetadata`] from an [`IbcChannel`] /// /// This is a fallback option if the ICA controller is not provided with the /// handshake version metadata by the relayer. It first tries to load the - /// previous version of the IcaMetadata from the store, and if it fails, + /// previous version of the [`IcaMetadata`] from the store, and if it fails, /// it uses a stargate query to get the counterparty connection id. /// Stargate queries are not universally supported, so this is a fallback option. + #[must_use] pub fn from_channel(deps: Deps, channel: &IbcChannel) -> Self { // If the the counterparty chain is using the fee middleware, and the this chain is not, // and the previous handshake was initiated with an empty version string, then the @@ -84,10 +87,11 @@ impl IcaMetadata { } } - use super::stargate::query; - let counterparty_connection_id = - query::counterparty_connection_id(&deps.querier, channel.connection_id.clone()) - .unwrap_or_default(); + let counterparty_connection_id = super::stargate::query::counterparty_connection_id( + &deps.querier, + channel.connection_id.clone(), + ) + .unwrap_or_default(); Self { version: ICA_VERSION.to_string(), controller_connection_id: channel.connection_id.clone(), @@ -95,13 +99,17 @@ impl IcaMetadata { // use a stargate query to get it. Stargate queries are not universally // supported, so this is a fallback option. host_connection_id: counterparty_connection_id, - address: "".to_string(), + address: String::new(), encoding: TxEncoding::Proto3Json, tx_type: "sdk_multi_msg".to_string(), } } - /// Validates the IcaMetadata + /// Validates the [`IcaMetadata`] + /// + /// # Errors + /// + /// Returns an error if the metadata is invalid. pub fn validate(&self, channel: &IbcChannel) -> Result<(), ContractError> { if self.version != ICA_VERSION { return Err(ContractError::InvalidVersion { @@ -125,7 +133,7 @@ impl IcaMetadata { Ok(()) } - /// Checks if the previous version of the IcaMetadata is equal to the current one + /// Checks if the previous version of the [`IcaMetadata`] is equal to the current one pub fn is_previous_version_equal(&self, previous_version: impl Into) -> bool { let maybe_previous_metadata: Result = serde_json_wasm::from_str(&previous_version.into()); @@ -155,9 +163,13 @@ impl ToString for TxEncoding { } /// Validates an ICA address +/// +/// # Errors +/// +/// Returns an error if the address is too long or contains invalid characters. fn validate_ica_address(address: &str) -> Result<(), ContractError> { const DEFAULT_MAX_LENGTH: usize = 128; - if address.len() > DEFAULT_MAX_LENGTH || !address.chars().all(|c| c.is_alphanumeric()) { + if address.len() > DEFAULT_MAX_LENGTH || !address.chars().all(char::is_alphanumeric) { return Err(ContractError::InvalidAddress {}); } Ok(()) @@ -200,7 +212,7 @@ mod tests { ICA_VERSION.to_string(), "connection-0".to_string(), "connection-1".to_string(), - "".to_string(), + String::new(), TxEncoding::Proto3Json, "sdk_multi_msg".to_string(), ) diff --git a/src/ibc/types/packet.rs b/src/ibc/types/packet.rs index 258a7a98..9d20d424 100644 --- a/src/ibc/types/packet.rs +++ b/src/ibc/types/packet.rs @@ -8,15 +8,16 @@ use cosmwasm_std::{to_json_binary, Env, IbcMsg, IbcTimeout, StdResult}; pub use cosmos_sdk_proto::ibc::applications::interchain_accounts::v1::CosmosTx; use cosmos_sdk_proto::traits::Message; -/// DEFAULT_TIMEOUT_SECONDS is the default timeout for [`IcaPacketData`] +/// `DEFAULT_TIMEOUT_SECONDS` is the default timeout for [`IcaPacketData`] pub const DEFAULT_TIMEOUT_SECONDS: u64 = 600; -/// IcaPacketData is comprised of a raw transaction, type of transaction and optional memo field. +/// `IcaPacketData` is comprised of a raw transaction, type of transaction and optional memo field. /// Currently, the host only supports [protobuf](super::metadata::TxEncoding::Protobuf) or /// [proto3json](super::metadata::TxEncoding::Proto3Json) serialized Cosmos transactions. /// /// If protobuf is used, then the raw transaction must encoded using /// [`CosmosTx`](cosmos_sdk_proto::ibc::applications::interchain_accounts::v1::CosmosTx). +#[allow(clippy::module_name_repetitions)] #[cw_serde] pub struct IcaPacketData { /// Type defines a classification of message issued from a controller @@ -39,7 +40,8 @@ pub struct IcaPacketData { } impl IcaPacketData { - /// Creates a new IcaPacketData + /// Creates a new [`IcaPacketData`] + #[must_use] pub fn new(data: Vec, memo: Option) -> Self { Self { r#type: 1, @@ -48,10 +50,10 @@ impl IcaPacketData { } } - /// Creates a new IcaPacketData from a list of JSON strings + /// Creates a new [`IcaPacketData`] from a list of JSON strings /// /// The messages must be serialized as JSON strings in the format expected by the ICA host. - /// The following is an example of a serialized [`IcaCosmosTx`](ica_cosmos_tx::IcaCosmosTx) with one legacy gov proposal message: + /// The following is an example with one legacy gov proposal message: /// /// ## Format /// @@ -73,21 +75,27 @@ impl IcaPacketData { /// ``` /// /// In this example, the proposer must be the ICA controller's address. - pub fn from_json_strings(messages: Vec, memo: Option) -> Self { + #[must_use] + pub fn from_json_strings(messages: &[String], memo: Option) -> Self { let combined_messages = messages.join(", "); - let json_txs = format!(r#"{{"messages": [{}]}}"#, combined_messages); + let json_txs = format!(r#"{{"messages": [{combined_messages}]}}"#); let data = json_txs.into_bytes(); Self::new(data, memo) } - /// Creates a new IcaPacketData from a list of [`cosmos_sdk_proto::Any`] messages + /// Creates a new [`IcaPacketData`] from a list of [`cosmos_sdk_proto::Any`] messages + #[must_use] pub fn from_proto_anys(messages: Vec, memo: Option) -> Self { let cosmos_tx = CosmosTx { messages }; let data = cosmos_tx.encode_to_vec(); Self::new(data, memo) } - /// Creates an IBC SendPacket message from the IcaPacketData + /// Creates an [`IbcMsg::SendPacket`] message from the [`IcaPacketData`] + /// + /// # Errors + /// + /// Returns an error if the [`IcaPacketData`] cannot be serialized to JSON. pub fn to_ibc_msg( &self, env: &Env, @@ -106,15 +114,15 @@ impl IcaPacketData { } } -/// contains the [`AcknowledgementData`] struct which is the acknowledgement to an ica packet +/// contains the [`Data`] struct which is the acknowledgement to an ica packet pub mod acknowledgement { use cosmwasm_std::Binary; - use super::*; + use super::cw_serde; - /// AcknowledgementData is the response to an ibc packet. It either contains a result or an error. + /// `Data` is the response to an ibc packet. It either contains a result or an error. #[cw_serde] - pub enum AcknowledgementData { + pub enum Data { /// Result is the result of a successful transaction. Result(Binary), /// Error is the error message of a failed transaction. @@ -125,7 +133,7 @@ pub mod acknowledgement { #[cfg(test)] mod tests { - use acknowledgement::AcknowledgementData; + use acknowledgement::Data as AcknowledgementData; use cosmwasm_std::{coins, from_json, Binary}; use crate::types::cosmos_msg::ExampleCosmosMessages; @@ -140,10 +148,10 @@ mod tests { } let packet_from_string = IcaPacketData::from_json_strings( - vec![r#"{"@type": "/cosmos.bank.v1beta1.MsgSend", "from_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "to_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "amount": [{"denom": "stake", "amount": "5000"}]}"#.to_string()], None); + &[r#"{"@type": "/cosmos.bank.v1beta1.MsgSend", "from_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "to_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "amount": [{"denom": "stake", "amount": "5000"}]}"#.to_string()], None); let packet_data = packet_from_string.data; - let cosmos_tx: TestCosmosTx = from_json(&Binary(packet_data)).unwrap(); + let cosmos_tx: TestCosmosTx = from_json(Binary(packet_data)).unwrap(); let expected = ExampleCosmosMessages::MsgSend { from_address: "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk".to_string(), @@ -163,7 +171,7 @@ mod tests { 123, 34, 114, 101, 115, 117, 108, 116, 34, 58, 34, 99, 51, 86, 106, 89, 50, 86, 122, 99, 119, 61, 61, 34, 125, ]); - let ack: AcknowledgementData = from_json(&cw_success_binary).unwrap(); + let ack: AcknowledgementData = from_json(cw_success_binary).unwrap(); assert_eq!( ack, AcknowledgementData::Result(Binary::from_base64("c3VjY2Vzcw==").unwrap()) @@ -173,7 +181,7 @@ mod tests { let error_bytes = br#"{"error":"ABCI code: 1: error handling packet: see events for details"}"#; let cw_error_binary = Binary(error_bytes.to_vec()); - let ack: AcknowledgementData = from_json(&cw_error_binary).unwrap(); + let ack: AcknowledgementData = from_json(cw_error_binary).unwrap(); assert_eq!( ack, AcknowledgementData::Error( diff --git a/src/ibc/types/stargate.rs b/src/ibc/types/stargate.rs index 644d7cec..d53faaea 100644 --- a/src/ibc/types/stargate.rs +++ b/src/ibc/types/stargate.rs @@ -18,7 +18,7 @@ use cosmwasm_std::Binary; /// Contains the stargate channel lifecycle helper methods. pub mod channel { - use super::*; + use super::{Binary, Message}; use cosmos_sdk_proto::ibc::core::channel::v1::{ Channel, Counterparty, MsgChannelOpenInit, Order, State, @@ -28,7 +28,7 @@ pub mod channel { use super::super::{keys, metadata}; - /// Creates a new MsgChannelOpenInit for an ica channel with the given contract address. + /// Creates a new [`MsgChannelOpenInit`] for an ica channel with the given contract address. /// Also generates the handshake version. /// If the counterparty port id is not provided, [`keys::HOST_PORT_ID`] is used. /// If the tx encoding is not provided, [`metadata::TxEncoding::Protobuf`] is used. @@ -62,7 +62,7 @@ pub mod channel { } } - /// Creates a new MsgChannelOpenInit for an ica channel. + /// Creates a new [`MsgChannelOpenInit`] for an ica channel. /// If the counterparty port id is not provided, [`keys::HOST_PORT_ID`] is used. fn new_ica_channel_open_init_msg( signer: impl Into, @@ -71,11 +71,8 @@ pub mod channel { counterparty_port_id: Option>, version: impl Into, ) -> MsgChannelOpenInit { - let counterparty_port_id = if let Some(port_id) = counterparty_port_id { - port_id.into() - } else { - keys::HOST_PORT_ID.into() - }; + let counterparty_port_id = + counterparty_port_id.map_or(keys::HOST_PORT_ID.into(), Into::into); MsgChannelOpenInit { port_id: port_id.into(), @@ -95,12 +92,16 @@ pub mod channel { } /// Contains the stargate query methods. pub mod query { - use super::*; + use super::{Binary, ContractError, Message}; use cosmos_sdk_proto::ibc::core::connection::v1::QueryConnectionRequest; use cosmwasm_std::{Empty, QuerierWrapper, QueryRequest}; /// Queries the counterparty connection id using stargate queries. + /// + /// # Errors + /// + /// Returns an error if the query fails. pub fn counterparty_connection_id( querier: &QuerierWrapper, connection_id: impl Into, @@ -119,25 +120,26 @@ pub mod query { /// Contains the types used in query responses. mod response { - /// QueryConnectionResponse is the response type for the Query/Connection RPC + /// `QueryConnectionResponse` is the response type for the Query/Connection RPC /// method. Besides the connection end, it includes a proof and the height from /// which the proof was retrieved. + #[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] pub struct QueryConnectionResponse { /// connection associated with the request identifier pub connection: ConnectionEnd, } - /// ConnectionEnd defines a stateful object on a chain connected to another + /// `ConnectionEnd` defines a stateful object on a chain connected to another /// separate one. - /// NOTE: there must only be 2 defined ConnectionEnds to establish + /// NOTE: there must only be 2 defined `ConnectionEnds` to establish /// a connection between two chains. #[derive(Clone, PartialEq, Debug, serde::Deserialize, serde::Serialize)] pub struct ConnectionEnd { /// client associated with this connection. pub client_id: String, /// IBC version which can be utilised to determine encodings or protocols for - /// channels or packets utilising this connection. + /// channels or packets utilizing this connection. pub versions: Vec, /// current state of the connection end. pub state: String, @@ -149,7 +151,7 @@ pub mod query { pub delay_period: String, } - /// Counterparty defines the counterparty chain associated with a connection end. + /// `Counterparty` defines the counterparty chain associated with a connection end. #[derive(Clone, PartialEq, Debug, serde::Deserialize, serde::Serialize)] pub struct Counterparty { /// identifies the client on the counterparty chain associated with a given @@ -162,7 +164,7 @@ pub mod query { pub prefix: MerklePrefix, } - /// MerklePrefix is merkle path prefixed to the key. + /// `MerklePrefix` is merkle path prefixed to the key. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, Debug, serde::Deserialize, serde::Serialize)] pub struct MerklePrefix { @@ -171,8 +173,9 @@ pub mod query { pub key_prefix: String, } - /// Version defines the versioning scheme used to negotiate the IBC verison in + /// `Version` defines the versioning scheme used to negotiate the IBC verison in /// the connection handshake. + #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, Debug, serde::Deserialize, serde::Serialize)] pub struct Version { /// unique version identifier diff --git a/src/lib.rs b/src/lib.rs index 2a100123..6394c836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![doc = include_str!("../README.md")] #![deny(missing_docs)] +#![deny(clippy::nursery, clippy::pedantic, warnings)] #[cfg(feature = "export")] pub mod contract; diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs index 45736a5c..5ee2a681 100644 --- a/src/types/callbacks.rs +++ b/src/types/callbacks.rs @@ -8,7 +8,9 @@ use cosmwasm_std::{ to_json_binary, Addr, Binary, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg, }; -use crate::ibc::types::{metadata::TxEncoding, packet::acknowledgement::AcknowledgementData}; +use crate::ibc::types::{ + metadata::TxEncoding, packet::acknowledgement::Data as AcknowledgementData, +}; /// IcaControllerCallbackMsg is the type of message that this contract can send to other contracts. #[cw_serde] @@ -45,13 +47,21 @@ pub enum IcaControllerCallbackMsg { impl IcaControllerCallbackMsg { /// serializes the message + /// + /// # Errors + /// + /// This function returns an error if the message cannot be serialized. pub fn into_json_binary(self) -> StdResult { let msg = ReceiverExecuteMsg::ReceiveIcaCallback(self); to_json_binary(&msg) } - /// into_cosmos_msg converts this message into a WasmMsg::Execute message to be sent to the - /// named contract. + /// `into_cosmos_msg` converts this message into a [`CosmosMsg`] message to be sent to + /// the named contract. + /// + /// # Errors + /// + /// This function returns an error if the message cannot be serialized. pub fn into_cosmos_msg(self, contract_addr: impl Into) -> StdResult> where C: Clone + std::fmt::Debug + PartialEq, diff --git a/src/types/cosmos_msg.rs b/src/types/cosmos_msg.rs index 228f3bf3..0e45b36a 100644 --- a/src/types/cosmos_msg.rs +++ b/src/types/cosmos_msg.rs @@ -3,7 +3,7 @@ use cosmwasm_std::Coin; -/// ExampleCosmosMessages is a list of Cosmos messages that can be sent to the ICA host if the channel handshake is +/// `ExampleCosmosMessages` is a list of Cosmos messages that can be sent to the ICA host if the channel handshake is /// completed with the [`proto3json`](crate::ibc::types::metadata::TxEncoding::Proto3Json) encoding format. /// /// This enum corresponds to the [Any](https://github.com/cosmos/cosmos-sdk/blob/v0.47.3/codec/types/any.go#L11-L52) @@ -13,7 +13,7 @@ use cosmwasm_std::Coin; /// /// In general, this ICA controller should be used with custom messages and **not with the messages defined here**. /// The messages defined here are to demonstrate how an ICA controller can be used with registered -/// ExampleCosmosMessages (in case the contract is a DAO with **predefined actions**) +/// `ExampleCosmosMessages` (in case the contract is a DAO with **predefined actions**) /// /// This enum does not derive [Deserialize](serde::Deserialize), see issue /// [#1443](https://github.com/CosmWasm/cosmwasm/issues/1443) @@ -109,7 +109,7 @@ impl ToString for ExampleCosmosMessages { } mod msg_transfer { - #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)] + #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Height { pub revision_number: u64, pub revision_height: u64, diff --git a/src/types/error.rs b/src/types/error.rs index 3c17c39c..d8cdd390 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,12 +1,13 @@ -//! This module defines [ContractError]. +//! This module defines [`ContractError`]. use std::string::FromUtf8Error; use cosmwasm_std::StdError; use thiserror::Error; -/// ContractError is the error type returned by contract's functions. +/// `ContractError` is the error type returned by contract's functions. #[allow(missing_docs)] +#[allow(clippy::module_name_repetitions)] #[derive(Error, Debug)] pub enum ContractError { #[error("{0}")] diff --git a/src/types/keys.rs b/src/types/keys.rs index 9d95e836..dfe53acd 100644 --- a/src/types/keys.rs +++ b/src/types/keys.rs @@ -2,8 +2,8 @@ //! //! Contains key constants definitions for the contract such as version info for migrations. -/// CONTRACT_NAME is the name of the contract recorded with cw2 +/// `CONTRACT_NAME` is the name of the contract recorded with cw2 pub const CONTRACT_NAME: &str = "crates.io:cw-ica-controller"; -/// CONTRACT_VERSION is the version of the cargo package. +/// `CONTRACT_VERSION` is the version of the cargo package. /// This is also the version of the contract recorded in cw2 pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src/types/mod.rs b/src/types/mod.rs index b35d68a0..d9f47a50 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,6 +4,7 @@ pub mod callbacks; pub mod cosmos_msg; mod error; pub mod keys; +#[allow(clippy::module_name_repetitions)] pub mod msg; pub mod state; diff --git a/src/types/msg.rs b/src/types/msg.rs index 5fd3ba53..a8830415 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -98,7 +98,7 @@ pub struct MigrateMsg {} /// Option types for other messages. pub mod options { - use super::*; + use super::cw_serde; use crate::ibc::types::{keys::HOST_PORT_ID, metadata::TxEncoding}; /// The message used to provide the MsgChannelOpenInit with the required data. @@ -117,13 +117,15 @@ pub mod options { impl ChannelOpenInitOptions { /// Returns the counterparty port id. + #[must_use] pub fn counterparty_port_id(&self) -> String { self.counterparty_port_id .clone() - .unwrap_or(HOST_PORT_ID.to_string()) + .unwrap_or_else(|| HOST_PORT_ID.to_string()) } /// Returns the tx encoding. + #[must_use] pub fn tx_encoding(&self) -> TxEncoding { self.tx_encoding.clone().unwrap_or(TxEncoding::Protobuf) } diff --git a/src/types/state.rs b/src/types/state.rs index d4bef8a5..d2a2f959 100644 --- a/src/types/state.rs +++ b/src/types/state.rs @@ -6,8 +6,10 @@ use cw_storage_plus::Item; use super::ContractError; -pub use channel::{ChannelState, ChannelStatus}; -pub use contract::{CallbackCounter, ContractState}; +#[allow(clippy::module_name_repetitions)] +pub use channel::{State as ChannelState, Status as ChannelStatus}; +#[allow(clippy::module_name_repetitions)] +pub use contract::{CallbackCounter, State as ContractState}; /// The item used to store the state of the IBC application. pub const STATE: Item = Item::new("state"); @@ -21,11 +23,11 @@ pub const CALLBACK_COUNTER: Item = Item::new("callback_counter" mod contract { use crate::ibc::types::metadata::TxEncoding; - use super::*; + use super::{cw_serde, Addr, ContractError}; - /// ContractState is the state of the IBC application. + /// State is the state of the contract. #[cw_serde] - pub struct ContractState { + pub struct State { /// The address of the admin of the IBC application. pub admin: Addr, /// The Interchain Account (ICA) info needed to send packets. @@ -40,9 +42,10 @@ mod contract { pub callback_address: Option, } - impl ContractState { - /// Creates a new ContractState - pub fn new(admin: Addr, callback_address: Option) -> Self { + impl State { + /// Creates a new [`State`] + #[must_use] + pub const fn new(admin: Addr, callback_address: Option) -> Self { Self { admin, ica_info: None, @@ -53,8 +56,12 @@ mod contract { } /// Checks if the address is the admin - pub fn verify_admin(&self, sender: impl Into) -> Result<(), ContractError> { - if self.admin == sender.into() { + /// + /// # Errors + /// + /// Returns an error if the address is not the admin. + pub fn verify_admin(&self, address: impl Into) -> Result<(), ContractError> { + if self.admin == address.into() { Ok(()) } else { Err(ContractError::Unauthorized {}) @@ -62,7 +69,11 @@ mod contract { } /// Checks if channel open init is allowed - pub fn verify_open_init_allowed(&self) -> Result<(), ContractError> { + /// + /// # Errors + /// + /// Returns an error if channel open init is not allowed. + pub const fn verify_open_init_allowed(&self) -> Result<(), ContractError> { if self.allow_channel_open_init { Ok(()) } else { @@ -71,12 +82,14 @@ mod contract { } /// Gets the ICA info + /// + /// # Errors + /// + /// Returns an error if the ICA info is not set. pub fn get_ica_info(&self) -> Result { - if let Some(ica_info) = &self.ica_info { - Ok(ica_info.clone()) - } else { - Err(ContractError::IcaInfoNotSet {}) - } + self.ica_info + .as_ref() + .map_or(Err(ContractError::IcaInfoNotSet {}), |s| Ok(s.clone())) } /// Disables channel open init @@ -127,7 +140,7 @@ mod contract { } impl IcaInfo { - /// Creates a new IcaInfo + /// Creates a new [`IcaInfo`] pub fn new( ica_address: impl Into, channel_id: impl Into, @@ -160,11 +173,11 @@ mod contract { } mod channel { - use super::*; + use super::{cw_serde, IbcChannel}; - /// ChannelState is the state of the IBC channel. + /// Status is the status of an IBC channel. #[cw_serde] - pub enum ChannelStatus { + pub enum Status { /// Uninitialized is the default state of the channel. #[serde(rename = "STATE_UNINITIALIZED_UNSPECIFIED")] Uninitialized, @@ -182,33 +195,35 @@ mod channel { Closed, } - /// ContractChannelState is the state of the IBC application's channel. + /// State is the state of the IBC application's channel. /// This application only supports one channel. #[cw_serde] - pub struct ChannelState { + pub struct State { /// The IBC channel, as defined by cosmwasm. pub channel: IbcChannel, /// The status of the channel. - pub channel_status: ChannelStatus, + pub channel_status: Status, } - impl ChannelState { - /// Creates a new ChannelState - pub fn new_open_channel(channel: IbcChannel) -> Self { + impl State { + /// Creates a new [`ChannelState`] + #[must_use] + pub const fn new_open_channel(channel: IbcChannel) -> Self { Self { channel, - channel_status: ChannelStatus::Open, + channel_status: Status::Open, } } /// Checks if the channel is open + #[must_use] pub fn is_open(&self) -> bool { - self.channel_status == ChannelStatus::Open + self.channel_status == Status::Open } /// Closes the channel pub fn close(&mut self) { - self.channel_status = ChannelStatus::Closed; + self.channel_status = Status::Closed; } } } @@ -259,7 +274,7 @@ mod tests { let serialized = cosmwasm_std::to_json_binary(&mock_state).unwrap(); - let deserialized: v0_1_3::ContractState = cosmwasm_std::from_json(&serialized).unwrap(); + let deserialized: v0_1_3::ContractState = cosmwasm_std::from_json(serialized).unwrap(); let exp_state = v0_1_3::ContractState { admin: Addr::unchecked("admin"), @@ -280,7 +295,7 @@ mod tests { let serialized = cosmwasm_std::to_json_binary(&mock_state).unwrap(); - let deserialized: ContractState = cosmwasm_std::from_json(&serialized).unwrap(); + let deserialized: ContractState = cosmwasm_std::from_json(serialized).unwrap(); let exp_state = ContractState { admin: Addr::unchecked("admin"), diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index bfb27777..e993cbfd 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -244,7 +244,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.2.0" +version = "0.3.0" dependencies = [ "cosmos-sdk-proto", "cosmwasm-schema", diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 6f8cc79a..8a47cacd 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -181,7 +181,7 @@ mod execute { amount: cosmwasm_std::coins(100, "stake"), } .to_string(); - IcaPacketData::from_json_strings(vec![predefined_json_message], None) + IcaPacketData::from_json_strings(&[predefined_json_message], None) } };