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

Host for cosmos #9

Draft
wants to merge 19 commits into
base: contracts-to-own-folder
Choose a base branch
from
Draft
775 changes: 761 additions & 14 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ members = [
"multiversx-side/connection",
"multiversx-side/connection/meta",
"multiversx-side/host",
"multiversx-side/host/meta"
"multiversx-side/host/meta",
"cosmos-side/host2"
]
4 changes: 4 additions & 0 deletions cosmos-side/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[alias]
wasm = "build --target wasm32-unknown-unknown --release --lib"
wasm-debug = "build --target wasm32-unknown-unknown --lib"
schema = "run schema"
18 changes: 18 additions & 0 deletions cosmos-side/common/common-modules2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "common-modules2"
version = "0.0.0"
authors = ["Dorin Iancu <[email protected]>"]
edition = "2021"

[lib]
path = "src/lib.rs"

[dependencies]
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
cosmwasm-std = "2.1.4"
cosmwasm-schema = "2.1.4"
cw-storage-plus = "2.0.0"
keccak-hash = "0.11.0"

[dependencies.common-types2]
path = "../common-types2"
32 changes: 32 additions & 0 deletions cosmos-side/common/common-modules2/src/client_lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use common_types2::ClientType;

// TODO: Change this if needed
const MAX_CLIENT_TYPE_LEN: usize = 128;

pub fn is_valid_client_type(client_type: &ClientType) -> bool {
let len = client_type.len();
if len == 0 || len > MAX_CLIENT_TYPE_LEN {
return false;
}

let first_char = client_type[0];
if !first_char.is_ascii_lowercase() {
return false;
}

let last_char = client_type[len - 1];
if !last_char.is_ascii_lowercase() && !last_char.is_ascii_digit() {
return false;
}

// clippy suggestion is dumb
#[allow(clippy::needless_range_loop)]
for i in 1..len - 1 {
let character = client_type[i];
if !character.is_ascii_lowercase() && !character.is_ascii_digit() && character != b'-' {
return false;
}
}

true
}
30 changes: 30 additions & 0 deletions cosmos-side/common/common-modules2/src/host_lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use common_types2::PortId;

const MIN_PORT_LEN: usize = 2;
const MAX_PORT_LEN: usize = 128;
const SPECIAL_CHARS: &[u8] = b"._+-#[]<>";

/// check if the string consist of characters in one of the following categories only:
///
/// - Alphanumeric
///
/// - `.`, `_`, `+`, `-`, `#`
///
/// - `[`, `]`, `<`, `>`
pub fn is_valid_port_id(port_id: &PortId) -> bool {
let port_len = port_id.len();

// Clippy suggestion makes code unreadable
#[allow(clippy::manual_range_contains)]
if port_len < MIN_PORT_LEN || port_len > MAX_PORT_LEN {
return false;
}

for character in port_id {
if !character.is_ascii_alphanumeric() && !SPECIAL_CHARS.contains(character) {
return false;
}
}

true
}
3 changes: 3 additions & 0 deletions cosmos-side/common/common-modules2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod client_lib;
pub mod host_lib;
pub mod utils;
97 changes: 97 additions & 0 deletions cosmos-side/common/common-modules2/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use common_types2::{channel_types::channel, UnixTimestamp};
use common_types2::{Hash, HASH_LENGTH};
use cosmwasm_std::{Addr, Env, StdError, StdResult, Storage};
use cw_storage_plus::{Item, Map, PrimaryKey};
use keccak_hash::keccak_buffer;
use serde::de::DeserializeOwned;
use serde::Serialize;

#[macro_export]
macro_rules! require {
($expression:expr, $($msg_tokens:tt),+ $(,)?) => {
if (!($expression)) {
return Err(cosmwasm_std::StdError::generic_err($($msg_tokens),+));
}
};
}

const NANO_SECONDS_MULT: u64 = 1_000_000_000;
const EMPTY_HASH: &[u8; HASH_LENGTH] = &[0u8; HASH_LENGTH];

pub static UNEXPECTED_CHANNEL_STATE_ERR_MSG: &str = "Unexpected channel state";

pub fn require_valid_address(address: &Addr, env: &Env) -> StdResult<()> {
require!(address != env.contract.address, "Invalid address");

Ok(())
}

pub fn require_state_open(state: channel::State) -> StdResult<()> {
require!(
matches!(state, channel::State::Open),
UNEXPECTED_CHANNEL_STATE_ERR_MSG
);

Ok(())
}

pub fn checked_timestamp_to_unix_mul(timestamp: u64) -> StdResult<UnixTimestamp> {
match timestamp.checked_mul(NANO_SECONDS_MULT) {
Some(result) => Ok(result),
None => std_err("Overflow!!!"),
}
}

#[inline]
pub fn is_empty_hash(hash: &Hash) -> bool {
hash == EMPTY_HASH
}

#[inline]
pub fn std_err<T>(err_msg: &str) -> StdResult<T> {
Err(StdError::generic_err(err_msg))
}

pub fn vec_u8_to_str(input_vec: Vec<u8>) -> String {
match String::from_utf8(input_vec) {
Ok(v) => v,
Err(_) => panic!("Invalid UTF-8 sequence"),
}
}

pub fn keccak256(input_data: &Vec<u8>) -> Hash {
keccak_buffer(&mut input_data.as_slice()).unwrap().into()
}

// is_empty impls

pub trait IsEmptyStorageItem {
fn is_empty(&self, storage: &dyn Storage) -> bool;
}

pub trait IsEmptyStorageMap<'a, T: PrimaryKey<'a>> {
fn is_empty_at_key(&self, storage: &dyn Storage, key: &'a T) -> bool;
}

impl<T> IsEmptyStorageItem for Item<T>
where
T: Serialize + DeserializeOwned,
{
fn is_empty(&self, storage: &dyn Storage) -> bool {
let may_load_result = self.may_load(storage);

matches!(may_load_result, Ok(None))
}
}

impl<'a, T, U> IsEmptyStorageMap<'a, T> for Map<&'a T, U>
where
T: PrimaryKey<'a>,
U: Serialize + DeserializeOwned,
{
fn is_empty_at_key(&self, storage: &dyn Storage, key: &'a T) -> bool {
let may_load_result = self.may_load(storage, key);

matches!(may_load_result, Ok(None))
}
}
13 changes: 13 additions & 0 deletions cosmos-side/common/common-types2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "common-types2"
version = "0.0.0"
authors = ["Dorin Iancu <[email protected]>"]
edition = "2021"

[lib]
path = "src/lib.rs"

[dependencies]
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
cosmwasm-std = "2.1.4"
cosmwasm-schema = "2.1.4"
178 changes: 178 additions & 0 deletions cosmos-side/common/common-types2/src/channel_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
pub mod channel {
use cosmwasm_schema::cw_serde;

use crate::{channel_types::channel_counterparty, ConnectionHops, Sequence, Version};

pub static ORDERED: &[u8] = b"ORDER_ORDERED";
pub static UNORDERED: &[u8] = b"ORDER_UNORDERED";

#[cw_serde]
pub enum State {
UninitializedUnspecified,
Init,
TryOpen,
Open,
Closed,
Flushing,
FlushComplete,
}

#[cw_serde]
pub enum Order {
NoneUnspecified,
Unordered,
Ordered,
}

impl Order {
pub fn to_byte_slice(&self) -> &[u8] {
match *self {
Order::NoneUnspecified => {
panic!("Unknown channel order")
}
Order::Unordered => UNORDERED,
Order::Ordered => ORDERED,
}
}
}

#[cw_serde]
pub struct Data {
pub state: State,
pub ordering: Order,
pub counterparty: channel_counterparty::Data,
pub connection_hops: ConnectionHops,
pub version: Version,
pub upgrade_sequence: Sequence,
}
}

pub mod channel_counterparty {
use cosmwasm_schema::cw_serde;

use crate::{ChannelId, PortId};

#[cw_serde]
pub struct Data {
pub port_id: PortId,
pub channel_id: ChannelId,
}
}

pub mod height {
use cosmwasm_schema::cw_serde;

const U64_BYTES: usize = 8;

#[cw_serde]
#[derive(PartialOrd, Copy)]
pub struct Data {
pub revision_number: u64,
pub revision_height: u64,
}

impl Data {
pub fn is_zero(&self) -> bool {
self.revision_number == 0 && self.revision_height == 0
}

pub fn to_u128(&self) -> u128 {
let mut return_value = self.revision_number as u128;
return_value <<= U64_BYTES;
return_value |= self.revision_height as u128;

return_value
}
}

#[cfg(test)]
mod tests {
use core::cmp::Ordering;

use super::*;

impl Data {
fn new(revision_number: u64, revision_height: u64) -> Self {
Self {
revision_number,
revision_height,
}
}
}

#[test]
fn partial_ord_test() {
assert_eq!(
Data::new(0, 100).partial_cmp(&Data::new(1, 50)),
Some(Ordering::Less)
);
assert_eq!(
Data::new(0, 100).partial_cmp(&Data::new(0, 100)),
Some(Ordering::Equal)
);
assert_eq!(
Data::new(0, 100).partial_cmp(&Data::new(0, 50)),
Some(Ordering::Greater)
);
assert_eq!(
Data::new(0, 50).partial_cmp(&Data::new(1, 50)),
Some(Ordering::Less)
);
}
}
}

pub mod timeout {
use cosmwasm_schema::cw_serde;

use crate::channel_types::height;
use crate::UnixTimestamp;

#[cw_serde]
pub struct Data {
pub height: height::Data,
pub timestamp: UnixTimestamp,
}
}

pub mod upgrade {
use cosmwasm_schema::cw_serde;

use crate::Sequence;

use super::{timeout, upgrade_fields};

#[cw_serde]
pub struct Data {
pub fields: upgrade_fields::Data,
pub timeout: timeout::Data,
pub next_sequence_send: Sequence,
}
}

pub mod upgrade_fields {
use cosmwasm_schema::cw_serde;

use crate::{ConnectionHops, Version};

use super::channel;

#[cw_serde]
pub struct Data {
pub ordering: channel::Order,
pub connection_hops: ConnectionHops,
pub version: Version,
}
}

pub mod error_receipt {
use cosmwasm_schema::cw_serde;

use crate::Sequence;

#[cw_serde]
pub struct Data {
pub sequence: Sequence,
pub message: Vec<u8>,
}
}
Loading
Loading