Skip to content

Commit

Permalink
Merge pull request #406 from icon-project/feat/stellar-cluster-connec…
Browse files Browse the repository at this point in the history
…tion

feat: stellar cluster connection
  • Loading branch information
gcranju authored Nov 29, 2024
2 parents a1e39af + c7305ab commit 9cc11c4
Show file tree
Hide file tree
Showing 17 changed files with 1,160 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/stellar-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions contracts/soroban/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions contracts/soroban/contracts/cluster-connection/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
185 changes: 185 additions & 0 deletions contracts/soroban/contracts/cluster-connection/src/contract.rs
Original file line number Diff line number Diff line change
@@ -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<Address, ContractError> {
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<Address, ContractError> {
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<BytesN<65>>,
) -> 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<BytesN<65>>, 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<u32, ContractError> {
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<Vec<BytesN<65>>, ContractError> {
let validators = storage::get_validators(&env).unwrap();
Ok(validators)
}

pub fn get_relayer(env: Env) -> Result<Address, ContractError> {
let address = storage::relayer(&env)?;
Ok(address)
}

pub fn get_fee(env: Env, network_id: String, response: bool) -> Result<u128, ContractError> {
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(())
}
}
18 changes: 18 additions & 0 deletions contracts/soroban/contracts/cluster-connection/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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,
}
19 changes: 19 additions & 0 deletions contracts/soroban/contracts/cluster-connection/src/event.rs
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit 9cc11c4

Please sign in to comment.