diff --git a/.github/workflows/stellar-build-and-test.yml b/.github/workflows/stellar-build-and-test.yml
index bd23e862..9c2cffe0 100644
--- a/.github/workflows/stellar-build-and-test.yml
+++ b/.github/workflows/stellar-build-and-test.yml
@@ -27,7 +27,7 @@ jobs:
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
- toolchain: 1.79.0
+ toolchain: 1.81.0
target: wasm32-unknown-unknown
override: true
profile: minimal
diff --git a/Cargo.lock b/Cargo.lock
index 3c98cba3..224d4770 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -197,6 +197,14 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "cluster-connection"
+version = "0.0.0"
+dependencies = [
+ "soroban-rlp",
+ "soroban-sdk",
+]
+
[[package]]
name = "common"
version = "0.1.0"
diff --git a/contracts/soroban/Cargo.lock b/contracts/soroban/Cargo.lock
index 9ac56a3a..fbf3411e 100644
--- a/contracts/soroban/Cargo.lock
+++ b/contracts/soroban/Cargo.lock
@@ -153,6 +153,14 @@ dependencies = [
"windows-targets",
]
+[[package]]
+name = "cluster-connection"
+version = "0.0.0"
+dependencies = [
+ "soroban-rlp",
+ "soroban-sdk",
+]
+
[[package]]
name = "const-oid"
version = "0.9.6"
diff --git a/contracts/soroban/contracts/cluster-connection/Cargo.toml b/contracts/soroban/contracts/cluster-connection/Cargo.toml
new file mode 100644
index 00000000..e6f6b482
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "cluster-connection"
+version = "0.0.0"
+edition = "2021"
+publish = false
+
+[lib]
+crate-type = ["cdylib"]
+doctest = false
+
+[dependencies]
+soroban-sdk = { workspace = true, features = ["alloc"] }
+soroban-rlp = { path = "../../libs/soroban-rlp" }
+
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
diff --git a/contracts/soroban/contracts/cluster-connection/src/contract.rs b/contracts/soroban/contracts/cluster-connection/src/contract.rs
new file mode 100644
index 00000000..b02d54ee
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/contract.rs
@@ -0,0 +1,185 @@
+use soroban_sdk::{contract, contractimpl, token, Address, Bytes, BytesN, Env, String, Vec};
+
+use crate::{errors::ContractError, event, helpers, storage, types::InitializeMsg};
+
+#[contract]
+pub struct ClusterConnection;
+
+#[contractimpl]
+impl ClusterConnection {
+ pub fn initialize(env: Env, msg: InitializeMsg) -> Result<(), ContractError> {
+ storage::is_initialized(&env)?;
+
+ storage::store_native_token(&env, msg.native_token);
+ storage::store_conn_sn(&env, 0);
+ storage::store_relayer(&env, msg.relayer);
+ storage::store_admin(&env, msg.admin);
+ storage::store_xcall(&env, msg.xcall_address);
+ storage::store_upgrade_authority(&env, msg.upgrade_authority);
+ storage::store_validator_threshold(&env, 0);
+ storage::store_validators(&env, Vec::new(&env));
+
+ Ok(())
+ }
+
+ pub fn get_admin(env: Env) -> Result
{
+ let address = storage::admin(&env)?;
+ Ok(address)
+ }
+
+ pub fn set_admin(env: Env, address: Address) -> Result<(), ContractError> {
+ helpers::ensure_admin(&env)?;
+ storage::store_admin(&env, address);
+ Ok(())
+ }
+
+ pub fn get_upgrade_authority(env: Env) -> Result {
+ let address = storage::get_upgrade_authority(&env)?;
+ Ok(address)
+ }
+
+ pub fn set_upgrade_authority(env: &Env, address: Address) -> Result<(), ContractError> {
+ helpers::ensure_upgrade_authority(&env)?;
+ storage::store_upgrade_authority(&env, address);
+
+ Ok(())
+ }
+
+ pub fn set_relayer(env: Env, address: Address) -> Result<(), ContractError> {
+ helpers::ensure_admin(&env)?;
+ storage::store_relayer(&env, address);
+ Ok(())
+ }
+
+ pub fn send_message(
+ env: Env,
+ tx_origin: Address,
+ to: String,
+ sn: i64,
+ msg: Bytes,
+ ) -> Result<(), ContractError> {
+ helpers::ensure_xcall(&env)?;
+
+ let next_conn_sn = storage::get_next_conn_sn(&env);
+ storage::store_conn_sn(&env, next_conn_sn);
+
+ let mut fee: u128 = 0;
+ if sn >= 0 {
+ fee = helpers::get_network_fee(&env, to.clone(), sn > 0)?;
+ }
+ if fee > 0 {
+ helpers::transfer_token(&env, &tx_origin, &env.current_contract_address(), &fee)?;
+ }
+ event::send_message(&env, to, next_conn_sn, msg);
+
+ Ok(())
+ }
+
+ pub fn recv_message_with_signatures(
+ env: Env,
+ src_network: String,
+ conn_sn: u128,
+ msg: Bytes,
+ signatures: Vec>,
+ ) -> Result<(), ContractError> {
+ helpers::ensure_relayer(&env)?;
+
+ if !helpers::verify_signatures(&env, signatures, &src_network, &conn_sn, &msg){
+ return Err(ContractError::SignatureVerificationFailed);
+ };
+
+ if storage::get_sn_receipt(&env, src_network.clone(), conn_sn) {
+ return Err(ContractError::DuplicateMessage);
+ }
+ storage::store_receipt(&env, src_network.clone(), conn_sn);
+
+ helpers::call_xcall_handle_message(&env, &src_network, msg)?;
+ Ok(())
+ }
+
+ pub fn set_fee(
+ env: Env,
+ network_id: String,
+ message_fee: u128,
+ response_fee: u128,
+ ) -> Result<(), ContractError> {
+ helpers::ensure_relayer(&env)?;
+
+ storage::store_network_fee(&env, network_id, message_fee, response_fee);
+ Ok(())
+ }
+
+ pub fn claim_fees(env: Env) -> Result<(), ContractError> {
+ let admin = helpers::ensure_relayer(&env)?;
+
+ let token_addr = storage::native_token(&env)?;
+ let client = token::Client::new(&env, &token_addr);
+ let balance = client.balance(&env.current_contract_address());
+
+ client.transfer(&env.current_contract_address(), &admin, &balance);
+ Ok(())
+ }
+
+ pub fn update_validators(env: Env, pub_keys: Vec>, threshold: u32) -> Result<(), ContractError> {
+ helpers::ensure_admin(&env)?;
+ let mut validators = Vec::new(&env);
+
+ for address in pub_keys.clone() {
+ if !validators.contains(&address) {
+ validators.push_back(address);
+ }
+ }
+ if (validators.len() as u32) < threshold {
+ return Err(ContractError::ThresholdExceeded);
+
+ }
+ storage::store_validators(&env, pub_keys);
+ storage::store_validator_threshold(&env, threshold);
+ Ok(())
+ }
+
+ pub fn get_validators_threshold(env: Env) -> Result {
+ let threshold = storage::get_validators_threshold(&env).unwrap();
+ Ok(threshold)
+ }
+
+ pub fn set_validators_threshold(env: Env, threshold: u32) -> Result<(), ContractError> {
+ helpers::ensure_admin(&env)?;
+ let validators = storage::get_validators(&env).unwrap();
+ if (validators.len() as u32) < threshold {
+ return Err(ContractError::ThresholdExceeded);
+ }
+ storage::store_validator_threshold(&env, threshold);
+ Ok(())
+ }
+
+ pub fn get_validators(env: Env) -> Result>, ContractError> {
+ let validators = storage::get_validators(&env).unwrap();
+ Ok(validators)
+ }
+
+ pub fn get_relayer(env: Env) -> Result {
+ let address = storage::relayer(&env)?;
+ Ok(address)
+ }
+
+ pub fn get_fee(env: Env, network_id: String, response: bool) -> Result {
+ helpers::get_network_fee(&env, network_id, response)
+ }
+
+ pub fn get_receipt(env: Env, network_id: String, sn: u128) -> bool {
+ storage::get_sn_receipt(&env, network_id, sn)
+ }
+
+ pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> {
+ helpers::ensure_upgrade_authority(&env)?;
+ env.deployer().update_current_contract_wasm(new_wasm_hash);
+
+ Ok(())
+ }
+
+ pub fn extend_instance_storage(env: Env) -> Result<(), ContractError> {
+ storage::extend_instance(&env);
+ Ok(())
+ }
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/errors.rs b/contracts/soroban/contracts/cluster-connection/src/errors.rs
new file mode 100644
index 00000000..b4d50517
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/errors.rs
@@ -0,0 +1,18 @@
+use soroban_sdk::contracterror;
+
+#[contracterror]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
+#[repr(u32)]
+pub enum ContractError {
+ OnlyAdmin = 1,
+ Uninitialized = 2,
+ AlreadyInitialized = 3,
+ InsufficientFund = 4,
+ DuplicateMessage = 5,
+ NetworkNotSupported = 6,
+ CannotRemoveAdmin = 7,
+ ThresholdExceeded = 8,
+ ValidatorNotFound = 9,
+ ValidatorAlreadyAdded = 10,
+ SignatureVerificationFailed = 11,
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/event.rs b/contracts/soroban/contracts/cluster-connection/src/event.rs
new file mode 100644
index 00000000..6db6d215
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/event.rs
@@ -0,0 +1,19 @@
+#![allow(non_snake_case)]
+
+use soroban_sdk::{contracttype, Bytes, Env, String};
+
+#[contracttype]
+pub struct SendMsgEvent {
+ pub targetNetwork: String,
+ pub connSn: u128,
+ pub msg: Bytes,
+}
+
+pub(crate) fn send_message(e: &Env, targetNetwork: String, connSn: u128, msg: Bytes) {
+ let emit_message = SendMsgEvent {
+ targetNetwork,
+ connSn,
+ msg,
+ };
+ e.events().publish(("Message",), emit_message);
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/helpers.rs b/contracts/soroban/contracts/cluster-connection/src/helpers.rs
new file mode 100644
index 00000000..f37ed14f
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/helpers.rs
@@ -0,0 +1,132 @@
+use soroban_sdk::{token, vec, Address, Bytes, BytesN, Env, Map, String, Vec};
+use crate::{errors::ContractError, interfaces::interface_xcall::XcallClient, storage};
+use soroban_rlp::encoder;
+
+pub fn ensure_relayer(e: &Env) -> Result {
+ let relayer = storage::relayer(&e)?;
+ relayer.require_auth();
+
+ Ok(relayer)
+}
+
+pub fn ensure_admin(e: &Env) -> Result {
+ let admin = storage::admin(&e)?;
+ admin.require_auth();
+
+ Ok(admin)
+}
+
+pub fn ensure_upgrade_authority(e: &Env) -> Result {
+ let authority = storage::get_upgrade_authority(&e)?;
+ authority.require_auth();
+
+ Ok(authority)
+}
+
+pub fn ensure_xcall(e: &Env) -> Result {
+ let xcall = storage::get_xcall(&e)?;
+ xcall.require_auth();
+
+ Ok(xcall)
+}
+
+pub fn get_network_fee(
+ env: &Env,
+ network_id: String,
+ response: bool,
+) -> Result {
+ let mut fee = storage::get_msg_fee(&env, network_id.clone())?;
+ if response {
+ fee += storage::get_res_fee(&env, network_id)?;
+ }
+
+ Ok(fee)
+}
+
+pub fn transfer_token(
+ e: &Env,
+ from: &Address,
+ to: &Address,
+ amount: &u128,
+) -> Result<(), ContractError> {
+ let native_token = storage::native_token(&e)?;
+ let client = token::Client::new(&e, &native_token);
+
+ client.transfer(&from, &to, &(*amount as i128));
+ Ok(())
+}
+
+pub fn verify_signatures(
+ e: &Env,
+ signatures: Vec>,
+ src_network: &String,
+ conn_sn: &u128,
+ message: &Bytes,
+) -> bool {
+ let validators = storage::get_validators(e).unwrap();
+ let threshold = storage::get_validators_threshold(e).unwrap();
+
+ if signatures.len() < threshold {
+ return false
+ }
+ let message_hash = e.crypto().keccak256(&get_encoded_message(e, src_network, conn_sn, message));
+ let mut unique_validators = Map::new(e);
+ let mut count = 0;
+
+
+ for sig in signatures.iter() {
+ let r_s_v = sig.to_array();
+ // Separate signature (r + s) and recovery ID
+ let signature_array: [u8; 64] = r_s_v[..64].try_into().unwrap(); // r + s part
+ let recovery_code = match r_s_v[64] {
+ rc if rc >= 27 => rc - 27,
+ rc => rc,
+ };
+ let signature = BytesN::<64>::from_array(e, &signature_array);
+
+ let public_key = e.crypto().secp256k1_recover(&message_hash, &signature, recovery_code as u32);
+
+ if validators.contains(&public_key) {
+ if !unique_validators.contains_key(public_key.clone()) {
+ unique_validators.set(public_key, count);
+ count += 1;
+ }
+ }
+ }
+ (unique_validators.len() as u32) >= threshold
+
+}
+
+
+pub fn get_encoded_message(e: &Env, src_network: &String, conn_sn: &u128, message: &Bytes) -> Bytes {
+ let mut list = vec![&e];
+ list.push_back(encoder::encode_string(&e, src_network.clone()));
+ list.push_back(encoder::encode_u128(&e, conn_sn.clone()));
+ list.push_back(encoder::encode(&e, message.clone()));
+
+ encoder::encode_list(&e, list, false)
+}
+
+#[cfg(not(test))]
+pub fn call_xcall_handle_message(e: &Env, nid: &String, msg: Bytes) -> Result<(), ContractError> {
+ let xcall_addr = storage::get_xcall(&e)?;
+ let client = XcallClient::new(&e, &xcall_addr);
+ client.handle_message(&e.current_contract_address(), nid, &msg);
+
+ Ok(())
+}
+
+#[cfg(test)]
+pub fn call_xcall_handle_message(_e: &Env, _nid: &String, _msg: Bytes) -> Result<(), ContractError> {
+ Ok(())
+}
+
+
+
+pub fn call_xcall_handle_error(e: &Env, sn: u128) -> Result<(), ContractError> {
+ let xcall_addr = storage::get_xcall(&e)?;
+ let client = XcallClient::new(&e, &xcall_addr);
+ client.handle_error(&e.current_contract_address(), &sn);
+
+ Ok(())
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/interfaces/interface_xcall.rs b/contracts/soroban/contracts/cluster-connection/src/interfaces/interface_xcall.rs
new file mode 100644
index 00000000..c49d6c9c
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/interfaces/interface_xcall.rs
@@ -0,0 +1,15 @@
+use soroban_sdk::{contractclient, Address, Bytes, Env, String};
+
+use crate::errors::ContractError;
+
+#[contractclient(name = "XcallClient")]
+pub trait IXcall {
+ fn handle_message(
+ env: Env,
+ sender: Address,
+ from_nid: String,
+ msg: Bytes,
+ ) -> Result<(), ContractError>;
+
+ fn handle_error(env: Env, sender: Address, sequence_no: u128) -> Result<(), ContractError>;
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/interfaces/mod.rs b/contracts/soroban/contracts/cluster-connection/src/interfaces/mod.rs
new file mode 100644
index 00000000..61f8fdfe
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/interfaces/mod.rs
@@ -0,0 +1 @@
+pub mod interface_xcall;
diff --git a/contracts/soroban/contracts/cluster-connection/src/lib.rs b/contracts/soroban/contracts/cluster-connection/src/lib.rs
new file mode 100644
index 00000000..1fd06073
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/lib.rs
@@ -0,0 +1,10 @@
+#![no_std]
+
+pub mod contract;
+pub mod errors;
+pub mod event;
+pub mod helpers;
+pub mod interfaces;
+pub mod storage;
+pub mod test;
+pub mod types;
diff --git a/contracts/soroban/contracts/cluster-connection/src/storage.rs b/contracts/soroban/contracts/cluster-connection/src/storage.rs
new file mode 100644
index 00000000..bdc017a1
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/storage.rs
@@ -0,0 +1,187 @@
+use soroban_sdk::{Address, BytesN, Env, String, Vec};
+
+use crate::{
+ errors::ContractError,
+ types::{NetworkFee, StorageKey},
+};
+
+const DAY_IN_LEDGERS: u32 = 17280; // assumes 5s a ledger
+
+const LEDGER_THRESHOLD_INSTANCE: u32 = DAY_IN_LEDGERS * 30; // ~ 30 days
+const LEDGER_BUMP_INSTANCE: u32 = LEDGER_THRESHOLD_INSTANCE + DAY_IN_LEDGERS; // ~ 31 days
+
+const LEDGER_THRESHOLD_PERSISTENT: u32 = DAY_IN_LEDGERS * 30; // ~ 30 days
+const LEDGER_BUMP_PERSISTENT: u32 = LEDGER_THRESHOLD_PERSISTENT + DAY_IN_LEDGERS; // ~ 31 days
+
+pub fn is_initialized(e: &Env) -> Result<(), ContractError> {
+ let initialized = e.storage().instance().has(&StorageKey::Admin);
+ if initialized {
+ Err(ContractError::AlreadyInitialized)
+ } else {
+ Ok(())
+ }
+}
+
+pub fn admin(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::Admin)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn relayer(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::Relayer)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn get_upgrade_authority(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::UpgradeAuthority)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn get_xcall(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::Xcall)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn native_token(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::Xlm)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn get_conn_sn(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::ConnSn)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn get_next_conn_sn(e: &Env) -> u128 {
+ let mut sn = e.storage().instance().get(&StorageKey::ConnSn).unwrap_or(0);
+ sn += 1;
+ sn
+}
+
+pub fn get_msg_fee(e: &Env, network_id: String) -> Result {
+ let key = StorageKey::NetworkFee(network_id);
+ let network_fee: NetworkFee = e
+ .storage()
+ .persistent()
+ .get(&key)
+ .unwrap_or(NetworkFee::default());
+
+ if network_fee.message_fee > 0 {
+ extend_persistent(e, &key);
+ }
+
+ Ok(network_fee.message_fee)
+}
+
+pub fn get_res_fee(e: &Env, network_id: String) -> Result {
+ let key = StorageKey::NetworkFee(network_id);
+ let network_fee: NetworkFee = e
+ .storage()
+ .persistent()
+ .get(&key)
+ .unwrap_or(NetworkFee::default());
+
+ if network_fee.response_fee > 0 {
+ extend_persistent(e, &key);
+ }
+
+ Ok(network_fee.response_fee)
+}
+
+pub fn get_sn_receipt(e: &Env, network_id: String, sn: u128) -> bool {
+ let key = StorageKey::Receipts(network_id, sn);
+ let is_received = e.storage().persistent().get(&key).unwrap_or(false);
+ if is_received {
+ extend_persistent(e, &key);
+ }
+
+ is_received
+}
+
+pub fn get_validators_threshold(e: &Env) -> Result {
+ e.storage()
+ .instance()
+ .get(&StorageKey::ValidatorThreshold)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn get_validators(e: &Env) -> Result>, ContractError> {
+ e.storage()
+ .instance()
+ .get(&StorageKey::Validators)
+ .ok_or(ContractError::Uninitialized)
+}
+
+pub fn store_receipt(e: &Env, network_id: String, sn: u128) {
+ let key = StorageKey::Receipts(network_id, sn);
+ e.storage().persistent().set(&key, &true);
+ extend_persistent(e, &key);
+}
+
+pub fn store_relayer(e: &Env, relayer: Address) {
+ e.storage().instance().set(&StorageKey::Relayer, &relayer);
+}
+
+pub fn store_admin(e: &Env, admin: Address) {
+ e.storage().instance().set(&StorageKey::Admin, &admin);
+}
+
+pub fn store_upgrade_authority(e: &Env, address: Address) {
+ e.storage()
+ .instance()
+ .set(&StorageKey::UpgradeAuthority, &address);
+}
+
+pub fn store_xcall(e: &Env, xcall: Address) {
+ e.storage().instance().set(&StorageKey::Xcall, &xcall);
+}
+
+pub fn store_native_token(e: &Env, address: Address) {
+ e.storage().instance().set(&StorageKey::Xlm, &address);
+}
+
+pub fn store_conn_sn(e: &Env, sn: u128) {
+ e.storage().instance().set(&StorageKey::ConnSn, &sn);
+}
+
+pub fn store_validator_threshold(e: &Env, threshold: u32) {
+ e.storage().instance().set(&StorageKey::ValidatorThreshold, &threshold);
+}
+
+pub fn store_validators(e: &Env, validators: Vec>) {
+ e.storage().instance().set(&StorageKey::Validators, &validators);
+}
+
+pub fn store_network_fee(e: &Env, network_id: String, message_fee: u128, response_fee: u128) {
+ let key = StorageKey::NetworkFee(network_id);
+ let network_fee = NetworkFee {
+ message_fee,
+ response_fee,
+ };
+ e.storage().persistent().set(&key, &network_fee);
+ extend_persistent(e, &key);
+}
+
+pub fn extend_instance(e: &Env) {
+ e.storage()
+ .instance()
+ .extend_ttl(LEDGER_THRESHOLD_INSTANCE, LEDGER_BUMP_INSTANCE);
+}
+
+pub fn extend_persistent(e: &Env, key: &StorageKey) {
+ e.storage()
+ .persistent()
+ .extend_ttl(key, LEDGER_THRESHOLD_PERSISTENT, LEDGER_BUMP_PERSISTENT);
+}
diff --git a/contracts/soroban/contracts/cluster-connection/src/test.rs b/contracts/soroban/contracts/cluster-connection/src/test.rs
new file mode 100644
index 00000000..fe375ba5
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/test.rs
@@ -0,0 +1,515 @@
+#![cfg(test)]
+
+extern crate std;
+
+mod xcall {
+ soroban_sdk::contractimport!(file = "../../target/wasm32-unknown-unknown/release/xcall.wasm");
+}
+
+use crate::{
+ contract::{ClusterConnection, ClusterConnectionClient},
+ event::SendMsgEvent,
+ storage,
+ types::InitializeMsg,
+};
+use soroban_sdk::{
+ symbol_short, testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation, Events}, token, vec, Address, Bytes, BytesN, Env, IntoVal, String, Symbol, Vec
+};
+
+pub struct TestContext {
+ env: Env,
+ xcall: Address,
+ contract: Address,
+ admin:Address,
+ relayer: Address,
+ native_token: Address,
+ token_admin: Address,
+ nid: String,
+ upgrade_authority: Address,
+}
+
+impl TestContext {
+ pub fn default() -> Self {
+ let env = Env::default();
+ let token_admin = Address::generate(&env);
+ let xcall = env.register_contract_wasm(None, xcall::WASM);
+ Self {
+ xcall: xcall.clone(),
+ contract: env.register_contract(None, ClusterConnection),
+ relayer: Address::generate(&env),
+ admin: Address::generate(&env),
+ native_token: env.register_stellar_asset_contract(token_admin.clone()),
+ nid: String::from_str(&env, "0x2.icon"),
+ upgrade_authority: Address::generate(&env),
+ env,
+ token_admin,
+ }
+ }
+
+ pub fn init_context(&self, client: &ClusterConnectionClient<'static>) {
+ self.env.mock_all_auths();
+
+ client.initialize(&InitializeMsg {
+ admin: self.admin.clone(),
+ relayer: self.relayer.clone(),
+ native_token: self.native_token.clone(),
+ xcall_address: self.xcall.clone(),
+ upgrade_authority: self.upgrade_authority.clone(),
+ });
+
+ }
+
+ pub fn init_send_message(&self, client: &ClusterConnectionClient<'static>) {
+ self.init_context(&client);
+ self.env.mock_all_auths_allowing_non_root_auth();
+
+ client.set_fee(&self.nid, &100, &100);
+ }
+}
+
+fn get_dummy_initialize_msg(env: &Env) -> InitializeMsg {
+ InitializeMsg {
+ admin: Address::generate(&env),
+ relayer: Address::generate(&env),
+ native_token: env.register_stellar_asset_contract(Address::generate(&env)),
+ xcall_address: Address::generate(&env),
+ upgrade_authority: Address::generate(&env),
+ }
+}
+
+
+#[test]
+fn test_initialize() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let admin = client.get_admin();
+ assert_eq!(admin, ctx.admin)
+}
+
+#[test]
+#[should_panic(expected = "HostError: Error(Contract, #3)")]
+fn test_initialize_fail_on_double_initialize() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+ ctx.init_context(&client);
+
+ client.initialize(&get_dummy_initialize_msg(&ctx.env));
+}
+
+#[test]
+fn test_set_admin() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let new_admin = Address::generate(&ctx.env);
+ client.set_admin(&new_admin);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.admin.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ symbol_short!("set_admin"),
+ (new_admin.clone(),).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ )
+}
+
+#[test]
+#[should_panic]
+fn test_set_admin_fail() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let new_admin = Address::generate(&ctx.env);
+ client.set_admin(&new_admin);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.xcall,
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ symbol_short!("set_admin"),
+ (new_admin.clone(),).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ )
+}
+
+#[test]
+fn test_set_upgrade_authority() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+ ctx.init_context(&client);
+
+ let new_upgrade_authority = Address::generate(&ctx.env);
+ client.set_upgrade_authority(&new_upgrade_authority);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.upgrade_authority.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ ctx.contract.clone(),
+ Symbol::new(&ctx.env, "set_upgrade_authority"),
+ (&new_upgrade_authority,).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+
+ let autorhity = client.get_upgrade_authority();
+ assert_eq!(autorhity, new_upgrade_authority);
+}
+
+#[test]
+fn test_set_fee() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let nid = String::from_str(&ctx.env, "icon");
+ client.set_fee(&nid, &10, &10);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.relayer,
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ symbol_short!("set_fee"),
+ (nid.clone(), 10_u128, 10_u128).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+ assert_eq!(client.get_fee(&nid, &true), 20);
+ assert_eq!(client.get_fee(&nid, &false), 10);
+}
+
+#[test]
+fn test_claim_fees() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let token_client = token::Client::new(&ctx.env, &ctx.native_token);
+ let asset_client = token::StellarAssetClient::new(&ctx.env, &ctx.native_token);
+
+ asset_client.mint(&ctx.contract, &1000);
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.token_admin,
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ ctx.native_token.clone(),
+ symbol_short!("mint"),
+ (&ctx.contract.clone(), 1000_i128,).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+ assert_eq!(token_client.balance(&ctx.contract), 1000);
+
+ client.claim_fees();
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.relayer.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ Symbol::new(&ctx.env, "claim_fees"),
+ ().into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+ assert_eq!(token_client.balance(&ctx.relayer), 1000);
+ assert_eq!(token_client.balance(&ctx.contract), 0);
+ assert_eq!(ctx.env.auths(), std::vec![]);
+}
+
+#[test]
+fn test_send_message() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+ ctx.init_send_message(&client);
+
+ let tx_origin = Address::generate(&ctx.env);
+
+ let asset_client = token::StellarAssetClient::new(&ctx.env, &ctx.native_token);
+ asset_client.mint(&tx_origin, &1000);
+
+ let msg = Bytes::from_array(&ctx.env, &[1, 2, 3]);
+ client.send_message(&tx_origin, &ctx.nid, &1, &msg);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![
+ (
+ ctx.xcall.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ Symbol::new(&ctx.env, "send_message"),
+ (tx_origin.clone(), ctx.nid.clone(), 1_i64, msg.clone()).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ ),
+ (
+ tx_origin.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ ctx.native_token.clone(),
+ Symbol::new(&ctx.env, "transfer"),
+ (tx_origin.clone(), ctx.contract.clone(), 200_i128).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )
+ ]
+ );
+
+ let emit_msg = SendMsgEvent {
+ targetNetwork: ctx.nid.clone(),
+ connSn: 1_u128,
+ msg: msg.clone(),
+ };
+ let event = vec![&ctx.env, ctx.env.events().all().last_unchecked()];
+ assert_eq!(
+ event,
+ vec![
+ &ctx.env,
+ (
+ client.address.clone(),
+ ("Message",).into_val(&ctx.env),
+ emit_msg.into_val(&ctx.env)
+ )
+ ]
+ )
+}
+
+#[test]
+#[should_panic(expected = "HostError: Error(Contract, #10)")]
+fn test_send_message_fail_for_insufficient_fee() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+ ctx.init_send_message(&client);
+
+ let sender = Address::generate(&ctx.env);
+
+ let asset_client = token::StellarAssetClient::new(&ctx.env, &ctx.native_token);
+ asset_client.mint(&sender, &100);
+
+ let msg = Bytes::from_array(&ctx.env, &[1, 2, 3]);
+ client.send_message(&sender, &ctx.nid, &1, &msg);
+}
+
+#[test]
+fn test_get_receipt_returns_false() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ let sequence_no = 1;
+ let receipt = client.get_receipt(&ctx.nid, &sequence_no);
+ assert_eq!(receipt, false);
+
+ ctx.env.as_contract(&ctx.contract, || {
+ storage::store_receipt(&ctx.env, ctx.nid.clone(), sequence_no);
+ });
+
+ let receipt = client.get_receipt(&ctx.nid, &sequence_no);
+ assert_eq!(receipt, true)
+}
+
+#[test]
+fn test_add_validator() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let val1 = [4, 174, 54, 168, 191, 216, 207, 101, 134, 243, 76, 104, 133, 40, 137, 72, 53, 245, 231, 193, 157, 54, 104, 155, 172, 84, 96, 101, 107, 97, 60, 94, 171, 241, 250, 152, 34, 18, 170, 39, 202, 236, 226, 58, 39, 8, 235, 60, 137, 54, 225, 50, 185, 253, 130, 197, 174, 226, 170, 75, 6, 145, 123, 87, 19];
+ let val2 = [4, 91, 65, 155, 222, 192, 210, 187, 193, 108, 232, 174, 20, 79, 248, 232, 37, 18, 63, 208, 203, 62, 54, 208, 7, 91, 109, 141, 229, 170, 181, 51, 136, 172, 143, 180, 194, 138, 138, 56, 67, 243, 7, 60, 218, 164, 12, 148, 63, 116, 115, 127, 192, 206, 164, 169, 95, 135, 119, 138, 255, 172, 115, 129, 144];
+ let val3 = [4, 248, 192, 175, 198, 228, 250, 20, 158, 23, 251, 176, 244, 208, 150, 71, 151, 27, 208, 22, 41, 30, 154, 198, 109, 10, 112, 142, 200, 47, 200, 213, 210, 172, 135, 141, 129, 183, 211, 241, 211, 127, 16, 19, 67, 159, 195, 235, 88, 164, 223, 47, 128, 47, 147, 28, 121, 28, 93, 129, 176, 144, 52, 243, 55];
+
+ let mut validators = Vec::new(&ctx.env);
+ validators.push_back(BytesN::from_array(&ctx.env, &val1));
+ validators.push_back(BytesN::from_array(&ctx.env, &val2));
+ validators.push_back(BytesN::from_array(&ctx.env, &val3));
+ client.update_validators(&validators, &3_u32);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.admin.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ Symbol::new(&ctx.env, "update_validators"),
+ (validators.clone(), 3_u32,).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+
+ assert_eq!(
+ client.get_validators(),
+ validators
+ );
+}
+
+
+#[test]
+fn test_set_threshold() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let val1 = [4, 174, 54, 168, 191, 216, 207, 101, 134, 243, 76, 104, 133, 40, 137, 72, 53, 245, 231, 193, 157, 54, 104, 155, 172, 84, 96, 101, 107, 97, 60, 94, 171, 241, 250, 152, 34, 18, 170, 39, 202, 236, 226, 58, 39, 8, 235, 60, 137, 54, 225, 50, 185, 253, 130, 197, 174, 226, 170, 75, 6, 145, 123, 87, 19];
+ let val2 = [4, 91, 65, 155, 222, 192, 210, 187, 193, 108, 232, 174, 20, 79, 248, 232, 37, 18, 63, 208, 203, 62, 54, 208, 7, 91, 109, 141, 229, 170, 181, 51, 136, 172, 143, 180, 194, 138, 138, 56, 67, 243, 7, 60, 218, 164, 12, 148, 63, 116, 115, 127, 192, 206, 164, 169, 95, 135, 119, 138, 255, 172, 115, 129, 144];
+ let val3 = [4, 248, 192, 175, 198, 228, 250, 20, 158, 23, 251, 176, 244, 208, 150, 71, 151, 27, 208, 22, 41, 30, 154, 198, 109, 10, 112, 142, 200, 47, 200, 213, 210, 172, 135, 141, 129, 183, 211, 241, 211, 127, 16, 19, 67, 159, 195, 235, 88, 164, 223, 47, 128, 47, 147, 28, 121, 28, 93, 129, 176, 144, 52, 243, 55];
+
+ let mut validators = Vec::new(&ctx.env);
+ validators.push_back(BytesN::from_array(&ctx.env, &val1));
+ validators.push_back(BytesN::from_array(&ctx.env, &val2));
+ validators.push_back(BytesN::from_array(&ctx.env, &val3));
+ client.update_validators(&validators, &3_u32);
+
+ let threshold: u32 = 2_u32;
+ client.set_validators_threshold(&threshold);
+
+ assert_eq!(
+ ctx.env.auths(),
+ std::vec![(
+ ctx.admin.clone(),
+ AuthorizedInvocation {
+ function: AuthorizedFunction::Contract((
+ client.address.clone(),
+ Symbol::new(&ctx.env, "set_validators_threshold"),
+ (threshold,).into_val(&ctx.env)
+ )),
+ sub_invocations: std::vec![]
+ }
+ )]
+ );
+ assert_eq!(client.get_validators_threshold(), threshold);
+
+ let threshold: u32 = 3_u32;
+ client.set_validators_threshold(&threshold);
+ assert_eq!(client.get_validators_threshold(), threshold);
+}
+
+
+#[test]
+fn test_receive_message() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let val1 = [4, 174, 54, 168, 191, 216, 207, 101, 134, 243, 76, 104, 133, 40, 137, 72, 53, 245, 231, 193, 157, 54, 104, 155, 172, 84, 96, 101, 107, 97, 60, 94, 171, 241, 250, 152, 34, 18, 170, 39, 202, 236, 226, 58, 39, 8, 235, 60, 137, 54, 225, 50, 185, 253, 130, 197, 174, 226, 170, 75, 6, 145, 123, 87, 19];
+ let val2 = [4, 91, 65, 155, 222, 192, 210, 187, 193, 108, 232, 174, 20, 79, 248, 232, 37, 18, 63, 208, 203, 62, 54, 208, 7, 91, 109, 141, 229, 170, 181, 51, 136, 172, 143, 180, 194, 138, 138, 56, 67, 243, 7, 60, 218, 164, 12, 148, 63, 116, 115, 127, 192, 206, 164, 169, 95, 135, 119, 138, 255, 172, 115, 129, 144];
+ let val3 = [4, 248, 192, 175, 198, 228, 250, 20, 158, 23, 251, 176, 244, 208, 150, 71, 151, 27, 208, 22, 41, 30, 154, 198, 109, 10, 112, 142, 200, 47, 200, 213, 210, 172, 135, 141, 129, 183, 211, 241, 211, 127, 16, 19, 67, 159, 195, 235, 88, 164, 223, 47, 128, 47, 147, 28, 121, 28, 93, 129, 176, 144, 52, 243, 55];
+
+ let mut validators = Vec::new(&ctx.env);
+ validators.push_back(BytesN::from_array(&ctx.env, &val1));
+ validators.push_back(BytesN::from_array(&ctx.env, &val2));
+ validators.push_back(BytesN::from_array(&ctx.env, &val3));
+ client.update_validators(&validators, &1_u32);
+
+ let conn_sn = 456456_u128;
+ let msg = Bytes::from_array(&ctx.env,&[104, 101, 108, 108, 111]);
+ let src_network = String::from_str(&ctx.env, "0x2.icon");
+
+ let mut signatures = Vec::new(&ctx.env);
+ signatures.push_back(BytesN::from_array(&ctx.env, &[35, 247, 49, 199, 251, 53, 83, 51, 115, 148, 35, 48, 85, 203, 185, 236, 5, 171, 221, 29, 247, 203, 190, 195, 208, 218, 204, 237, 88, 191, 91, 75, 48, 87, 108, 161, 75, 234, 147, 234, 65, 134, 233, 32, 249, 159, 43, 159, 86, 211, 1, 117, 176, 167, 53, 99, 34, 243, 165, 215, 93, 232, 67, 184, 27]));
+
+ client.recv_message_with_signatures(&src_network, &conn_sn, &msg, &signatures);
+}
+
+#[test]
+#[should_panic(expected = "HostError: Error(Contract, #11)")]
+fn test_receive_message_less_signatures() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let val1 = [4, 174, 54, 168, 191, 216, 207, 101, 134, 243, 76, 104, 133, 40, 137, 72, 53, 245, 231, 193, 157, 54, 104, 155, 172, 84, 96, 101, 107, 97, 60, 94, 171, 241, 250, 152, 34, 18, 170, 39, 202, 236, 226, 58, 39, 8, 235, 60, 137, 54, 225, 50, 185, 253, 130, 197, 174, 226, 170, 75, 6, 145, 123, 87, 19];
+ let val2 = [4, 91, 65, 155, 222, 192, 210, 187, 193, 108, 232, 174, 20, 79, 248, 232, 37, 18, 63, 208, 203, 62, 54, 208, 7, 91, 109, 141, 229, 170, 181, 51, 136, 172, 143, 180, 194, 138, 138, 56, 67, 243, 7, 60, 218, 164, 12, 148, 63, 116, 115, 127, 192, 206, 164, 169, 95, 135, 119, 138, 255, 172, 115, 129, 144];
+ let val3 = [4, 248, 192, 175, 198, 228, 250, 20, 158, 23, 251, 176, 244, 208, 150, 71, 151, 27, 208, 22, 41, 30, 154, 198, 109, 10, 112, 142, 200, 47, 200, 213, 210, 172, 135, 141, 129, 183, 211, 241, 211, 127, 16, 19, 67, 159, 195, 235, 88, 164, 223, 47, 128, 47, 147, 28, 121, 28, 93, 129, 176, 144, 52, 243, 55];
+
+ let mut validators = Vec::new(&ctx.env);
+ validators.push_back(BytesN::from_array(&ctx.env, &val1));
+ validators.push_back(BytesN::from_array(&ctx.env, &val2));
+ validators.push_back(BytesN::from_array(&ctx.env, &val3));
+ client.update_validators(&validators, &2_u32);
+
+ let conn_sn = 456456_u128;
+ let msg = Bytes::from_array(&ctx.env,&[104, 101, 108, 108, 111]);
+ let src_network = String::from_str(&ctx.env, "0x2.icon");
+
+ let mut signatures = Vec::new(&ctx.env);
+ signatures.push_back(BytesN::from_array(&ctx.env, &[35, 247, 49, 199, 251, 53, 83, 51, 115, 148, 35, 48, 85, 203, 185, 236, 5, 171, 221, 29, 247, 203, 190, 195, 208, 218, 204, 237, 88, 191, 91, 75, 48, 87, 108, 161, 75, 234, 147, 234, 65, 134, 233, 32, 249, 159, 43, 159, 86, 211, 1, 117, 176, 167, 53, 99, 34, 243, 165, 215, 93, 232, 67, 184, 27]));
+
+ client.recv_message_with_signatures(&src_network, &conn_sn, &msg, &signatures);
+}
+
+#[test]
+#[should_panic(expected = "HostError: Error(Contract, #11)")]
+fn test_receive_message_with_invalid_signature() {
+ let ctx = TestContext::default();
+ let client = ClusterConnectionClient::new(&ctx.env, &ctx.contract);
+
+ ctx.init_context(&client);
+
+ let val1 = [4, 174, 54, 168, 191, 216, 207, 101, 134, 243, 76, 104, 133, 40, 137, 72, 53, 245, 231, 193, 157, 54, 104, 155, 172, 84, 96, 101, 107, 97, 60, 94, 171, 241, 250, 152, 34, 18, 170, 39, 202, 236, 226, 58, 39, 8, 235, 60, 137, 54, 225, 50, 185, 253, 130, 197, 174, 226, 170, 75, 6, 145, 123, 87, 19];
+ let val2 = [4, 91, 65, 155, 222, 192, 210, 187, 193, 108, 232, 174, 20, 79, 248, 232, 37, 18, 63, 208, 203, 62, 54, 208, 7, 91, 109, 141, 229, 170, 181, 51, 136, 172, 143, 180, 194, 138, 138, 56, 67, 243, 7, 60, 218, 164, 12, 148, 63, 116, 115, 127, 192, 206, 164, 169, 95, 135, 119, 138, 255, 172, 115, 129, 144];
+ let val3 = [4, 248, 192, 175, 198, 228, 250, 20, 158, 23, 251, 176, 244, 208, 150, 71, 151, 27, 208, 22, 41, 30, 154, 198, 109, 10, 112, 142, 200, 47, 200, 213, 210, 172, 135, 141, 129, 183, 211, 241, 211, 127, 16, 19, 67, 159, 195, 235, 88, 164, 223, 47, 128, 47, 147, 28, 121, 28, 93, 129, 176, 144, 52, 243, 55];
+
+ let mut validators = Vec::new(&ctx.env);
+ validators.push_back(BytesN::from_array(&ctx.env, &val1));
+ validators.push_back(BytesN::from_array(&ctx.env, &val2));
+ validators.push_back(BytesN::from_array(&ctx.env, &val3));
+ client.update_validators(&validators, &1_u32);
+
+ let conn_sn = 456456_u128;
+ let msg = Bytes::from_array(&ctx.env,&[104, 100, 108, 108, 111]);
+ let src_network = String::from_str(&ctx.env, "0x2.icon");
+
+ let mut signatures = Vec::new(&ctx.env);
+ signatures.push_back(BytesN::from_array(&ctx.env, &[35, 247, 49, 199, 251, 53, 83, 51, 115, 148, 35, 48, 85, 203, 185, 236, 5, 171, 221, 29, 247, 203, 190, 195, 208, 218, 204, 237, 88, 191, 91, 75, 48, 87, 108, 161, 75, 234, 147, 234, 65, 134, 233, 32, 249, 159, 43, 159, 86, 211, 1, 117, 176, 167, 53, 99, 34, 243, 165, 215, 93, 232, 67, 184, 27]));
+
+ client.recv_message_with_signatures(&src_network, &conn_sn, &msg, &signatures);
+}
+
diff --git a/contracts/soroban/contracts/cluster-connection/src/types.rs b/contracts/soroban/contracts/cluster-connection/src/types.rs
new file mode 100644
index 00000000..bf5f551d
--- /dev/null
+++ b/contracts/soroban/contracts/cluster-connection/src/types.rs
@@ -0,0 +1,40 @@
+use soroban_sdk::{contracttype, Address, String};
+
+#[contracttype]
+#[derive(Clone)]
+pub enum StorageKey {
+ Xcall,
+ Relayer,
+ Admin,
+ UpgradeAuthority,
+ Xlm,
+ ConnSn,
+ NetworkFee(String),
+ Receipts(String, u128),
+ Validators,
+ ValidatorThreshold
+}
+
+#[contracttype]
+pub struct InitializeMsg {
+ pub relayer: Address,
+ pub admin: Address,
+ pub native_token: Address,
+ pub xcall_address: Address,
+ pub upgrade_authority: Address,
+}
+
+#[contracttype]
+pub struct NetworkFee {
+ pub message_fee: u128,
+ pub response_fee: u128,
+}
+
+impl NetworkFee {
+ pub fn default() -> Self {
+ Self {
+ message_fee: 0,
+ response_fee: 0,
+ }
+ }
+}
diff --git a/contracts/soroban/contracts/mock-dapp-multi/src/test.rs b/contracts/soroban/contracts/mock-dapp-multi/src/test.rs
index df83e062..6be6e775 100644
--- a/contracts/soroban/contracts/mock-dapp-multi/src/test.rs
+++ b/contracts/soroban/contracts/mock-dapp-multi/src/test.rs
@@ -1,4 +1,4 @@
-#![cfg(test)]
+// #![cfg(test)]
-mod contract;
-pub mod setup;
+// mod contract;
+// pub mod setup;
diff --git a/contracts/soroban/libs/soroban-rlp/src/utils.rs b/contracts/soroban/libs/soroban-rlp/src/utils.rs
index 403cac52..b359078c 100644
--- a/contracts/soroban/libs/soroban-rlp/src/utils.rs
+++ b/contracts/soroban/libs/soroban-rlp/src/utils.rs
@@ -53,7 +53,7 @@ pub fn bytes_to_u64(bytes: Bytes) -> u64 {
}
pub fn u128_to_bytes(env: &Env, number: u128) -> Bytes {
- let mut bytes = bytes!(&env, 0x00);
+ let mut bytes: Bytes = Bytes::new(&env);
let mut i = 15;
let mut leading_zero = true;
while i >= 0 {
diff --git a/scripts/optimize-stellar.sh b/scripts/optimize-stellar.sh
index b527d71f..691c6e4b 100755
--- a/scripts/optimize-stellar.sh
+++ b/scripts/optimize-stellar.sh
@@ -11,7 +11,7 @@ cargo build --target wasm32-unknown-unknown --release
for WASM in $build_directory/*.wasm; do
NAME=$(basename "$WASM" .wasm)${SUFFIX}.wasm
echo "Optimizing $NAME ... $WASM"
- stellar contract optimize --wasm "$WASM"
+ /usr/local/bin/stellar2 contract optimize --wasm "$WASM"
done
cd -