Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup definitions and functions for Interchain Accounts application #719

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ members = [
"ibc-apps/ics20-transfer",
"ibc-apps/ics721-nft-transfer/types",
"ibc-apps/ics721-nft-transfer",
"ibc-apps/ics27-interchain-accounts",
"ibc-apps",
"ibc-core/ics24-host/cosmos",
"ibc-data-types",
Expand Down Expand Up @@ -66,6 +67,7 @@ sha2 = { version = "0.10.8", default-features = false }
serde = { version = "1.0", default-features = false }
serde_json = { package = "serde-json-wasm", version = "1.0.1", default-features = false }
subtle-encoding = { version = "0.5", default-features = false }
cosmrs = { version = "0.16.0" }

# ibc dependencies
ibc = { version = "0.53.0", path = "./ibc", default-features = false }
Expand All @@ -89,8 +91,9 @@ ibc-client-cw = { version = "0.53.0", path = "./ibc-clients/cw-contex
ibc-client-tendermint = { version = "0.53.0", path = "./ibc-clients/ics07-tendermint", default-features = false }
ibc-client-tendermint-cw = { version = "0.53.0", path = "./ibc-clients/ics07-tendermint/cw-contract", default-features = false }

ibc-app-transfer = { version = "0.53.0", path = "./ibc-apps/ics20-transfer", default-features = false }
ibc-app-nft-transfer = { version = "0.53.0", path = "./ibc-apps/ics721-nft-transfer", default-features = false }
ibc-app-transfer = { version = "0.53.0", path = "./ibc-apps/ics20-transfer", default-features = false }
ibc-app-nft-transfer = { version = "0.53.0", path = "./ibc-apps/ics721-nft-transfer", default-features = false }
ibc-app-interchain-accounts = { version = "0.53.0", path = "./ibc-apps/ics27-interchain-accounts", default-features = false }

ibc-core-client-context = { version = "0.53.0", path = "./ibc-core/ics02-client/context", default-features = false }
ibc-core-client-types = { version = "0.53.0", path = "./ibc-core/ics02-client/types", default-features = false }
Expand Down
5 changes: 3 additions & 2 deletions ibc-apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ description = """
all-features = true

[dependencies]
ibc-app-transfer = { workspace = true }
ibc-app-nft-transfer = { workspace = true, optional = true, features = [ "std", "serde", "schema", "borsh", "parity-scale-codec" ] }
ibc-app-transfer = { workspace = true }
ibc-app-nft-transfer = { workspace = true, optional = true, features = [ "std", "serde", "schema", "borsh", "parity-scale-codec" ] }
ibc-app-interchain-accounts = { workspace = true }

[features]
default = [ "std" ]
Expand Down
62 changes: 62 additions & 0 deletions ibc-apps/ics27-interchain-accounts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[package]
name = "ibc-app-interchain-accounts"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
keywords = [ "blockchain", "cosmos", "ibc", "transfer", "ics20" ]
readme = "./../README.md"

description = """
Maintained by `ibc-rs`, contains the implementation of the ICS-20 Fungible Token Transfer
application logic and re-exports essential data structures and domain types from
`ibc-app-transfer-types` crate.
"""

[package.metadata.docs.rs]
all-features = true

[dependencies]
# external dependencies
serde = { workspace = true }
serde_json = { workspace = true, optional = true }
cosmrs = { workspace = true }
prost = { workspace = true }
sha2 = { workspace = true }

# ibc dependencies
ibc-core = { workspace = true }
ibc-app-transfer-types = { workspace = true }
ibc-proto = { workspace = true }

[dev-dependencies]
subtle-encoding = { workspace = true }

[features]
default = [ "std" ]
std = [
"ibc-app-transfer-types/std",
"ibc-core/std",
"serde_json/std",
]
serde = [
"ibc-app-transfer-types/serde",
"ibc-core/serde",
"serde_json",
]
schema = [
"ibc-app-transfer-types/schema",
"ibc-core/schema",
"serde",
"std",
]
borsh = [
"ibc-app-transfer-types/borsh",
"ibc-core/borsh",
]
parity-scale-codec = [
"ibc-app-transfer-types/parity-scale-codec",
"ibc-core/parity-scale-codec",
]
166 changes: 166 additions & 0 deletions ibc-apps/ics27-interchain-accounts/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use alloc::string::{String, ToString};
use alloc::vec;

use cosmrs::AccountId;
use ibc_core::host::types::identifiers::PortId;
use ibc_core::primitives::proto::Protobuf;
use ibc_core::primitives::Signer;
use ibc_proto::cosmos::auth::v1beta1::BaseAccount as RawBaseAccount;
use ibc_proto::google::protobuf::Any;
use ibc_proto::ibc::apps::interchain_accounts::v1::InterchainAccount as RawInterchainAccount;
use prost::Message;
use sha2::{Digest, Sha256};

use super::error::InterchainAccountError;
use super::MODULE_ID_STR;

/// Defines an interchain account type with a generic base account.
///
/// TODO: to put a note that we currently only support Cosmos-SDK driven chains.
#[derive(Clone, Debug)]
pub struct InterchainAccount<A: BaseAccount> {
/// The base account.
base_account: A,
/// The account owner.
owner: PortId,
}

impl<A: BaseAccount> InterchainAccount<A> {
/// Constructs a new interchain account instance.
pub fn new(base_account: A, owner: PortId) -> Self {
Self {
base_account,
owner,
}
}

/// Constructs a new interchain account with a Cosmos-SDK base account.
pub fn new_with_sdk_base_account(
address: AccountId,
owner: PortId,
) -> InterchainAccount<SdkBaseAccount> {
let acc = SdkBaseAccount {
address,
pub_key: Any {
type_url: String::new(),
value: vec![],
},
account_number: 0,
sequence: 0,
};
InterchainAccount::new(acc, owner)
}

pub fn address(&self) -> Signer {
self.base_account.address()
}
}

impl BaseAccount for SdkBaseAccount {
fn address(&self) -> Signer {
Signer::from(self.address.to_string())
}
}

impl Protobuf<RawInterchainAccount> for InterchainAccount<SdkBaseAccount> {}

impl TryFrom<RawInterchainAccount> for InterchainAccount<SdkBaseAccount> {
type Error = InterchainAccountError;

fn try_from(raw: RawInterchainAccount) -> Result<Self, Self::Error> {
Ok(InterchainAccount {
base_account: match raw.base_account {
Some(base_account) => SdkBaseAccount::try_from(base_account)?,
None => return Err(InterchainAccountError::not_found("base account")),
},
owner: raw.account_owner.parse().unwrap(),
})
}
}

impl From<InterchainAccount<SdkBaseAccount>> for RawInterchainAccount {
fn from(domain: InterchainAccount<SdkBaseAccount>) -> Self {
RawInterchainAccount {
base_account: Some(domain.base_account.into()),
account_owner: domain.owner.to_string(),
}
}
}

/// Defines the base account for Cosmos-SDK driven chains.
#[derive(Clone, Debug)]
pub struct SdkBaseAccount {
/// The address of the account.
pub address: AccountId,
/// The public key of the account.
pub pub_key: Any,
/// The account number.
pub account_number: u64,
/// The sequence number.
pub sequence: u64,
}

impl Protobuf<RawBaseAccount> for SdkBaseAccount {}

impl TryFrom<RawBaseAccount> for SdkBaseAccount {
type Error = InterchainAccountError;

fn try_from(raw: RawBaseAccount) -> Result<Self, Self::Error> {
// TODO: should we check anything here? regarding number and sequence?
Ok(SdkBaseAccount {
address: raw
.address
.parse()
.map_err(InterchainAccountError::source)?,
pub_key: match raw.pub_key {
Some(pub_key) => pub_key,
None => return Err(InterchainAccountError::not_found("missing base account")),
},
account_number: raw.account_number,
sequence: raw.sequence,
})
}
}

impl From<SdkBaseAccount> for RawBaseAccount {
fn from(domain: SdkBaseAccount) -> Self {
RawBaseAccount {
address: domain.address.to_string(),
pub_key: Some(domain.pub_key),
account_number: domain.account_number,
sequence: domain.sequence,
}
}
}

const TYPE_URL: &str = "/cosmos.auth.v1beta1.BaseAccount";

impl From<SdkBaseAccount> for Any {
fn from(account: SdkBaseAccount) -> Self {
let account = RawBaseAccount::from(account);
Any {
type_url: TYPE_URL.to_string(),
value: account.encode_to_vec(),
}
}
}

/// Enforces minimum definition requirement for a base account.
pub trait BaseAccount {
fn address(&self) -> Signer;
}

pub fn get_sdk_controller_account() -> Result<AccountId, InterchainAccountError> {
let mut hasher = Sha256::new();

hasher.update(MODULE_ID_STR.as_bytes());

let mut hash = hasher.finalize().to_vec();

hash.truncate(20);

let controller_account =
AccountId::new(MODULE_ID_STR, &hash).map_err(InterchainAccountError::source)?;

Ok(controller_account)
}
85 changes: 85 additions & 0 deletions ibc-apps/ics27-interchain-accounts/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use ibc_core::host::types::identifiers::{ChannelId, ConnectionId, PortId};
use ibc_core::host::{ExecutionContext, ValidationContext};
use ibc_core::primitives::Signer;

use super::account::{BaseAccount, InterchainAccount};
use super::error::InterchainAccountError;
use super::host::params::Params;

pub trait InterchainAccountValidationContext: ValidationContext {
type AccountId: TryFrom<Signer>;

/// Returns true if the controller functionality is enabled on the chain
fn is_controller_enabled(&self) -> bool;

/// Returns the active `ChannelId` from the store by the provided
/// `ConnectionId` and `PortId`
fn get_active_channel_id(
&self,
connection_id: &ConnectionId,
port_id: &PortId,
) -> Result<ChannelId, InterchainAccountError>;

/// Returns the parameters needed for functioning as a host chain
fn get_params(&self) -> Result<Params, InterchainAccountError>;

/// Returns the `AccountId` for the given address
fn get_interchain_account(
&self,
address: &Signer,
) -> Result<Self::AccountId, InterchainAccountError>;

/// Returns the InterchainAccount address from the store associated with
/// the provided ConnectionId and PortId
fn get_ica_address(
&self,
connection_id: &ConnectionId,
port_id: &PortId,
) -> Result<Signer, InterchainAccountError>;
}

pub trait InterchainAccountExecutionContext:
ExecutionContext + InterchainAccountValidationContext
{
/// Stores the active `ChannelId` to the store by the provided
/// `ConnectionId` and `PortId`
fn store_active_channel_id(
&mut self,
connection_id: ConnectionId,
port_id: PortId,
channel_id: ChannelId,
) -> Result<(), InterchainAccountError>;

/// Stores the parameters for functioning as a host chain
fn store_params(&mut self, params: Params) -> Result<(), InterchainAccountError>;

/// Generates a new interchain account address.
///
/// It uses the host `ConnectionId`, the controller `PortId`, and may also
/// (in case of Cosmos SDK chains) incorporate block dependent information.
fn generate_ica_address(
&self,
connection_id: ConnectionId,
port_id: PortId,
) -> Result<Signer, InterchainAccountError>;

/// Stores the interchain account address
fn store_ica_address(
&mut self,
connection_id: ConnectionId,
port_id: PortId,
interchain_account_address: Signer,
) -> Result<(), InterchainAccountError>;

/// Creates a new interchain account with the provided account information
fn new_interchain_account<A: BaseAccount>(
&mut self,
account: InterchainAccount<A>,
) -> Result<Self::AccountId, InterchainAccountError>;

/// Stores the created interchain account to the store
fn store_interchain_account(
&mut self,
account: Self::AccountId,
) -> Result<(), InterchainAccountError>;
}
Loading
Loading