From cfc2b0716789bde57a0e4e4b4fe7f9f5a65de581 Mon Sep 17 00:00:00 2001 From: gcranju <075bct064.ranju@pcampus.edu.np> Date: Sun, 6 Oct 2024 01:59:01 +0545 Subject: [PATCH 1/5] feat: sui cluster connection --- .../cluster_connection.move | 55 ++++ .../cluster_connection/cluster_entry.move | 66 +++++ .../cluster_connection/cluster_state.move | 273 ++++++++++++++++++ .../cluster_connection/signatures.move | 196 +++++++++++++ contracts/sui/xcall/sources/connections.move | 28 +- 5 files changed, 610 insertions(+), 8 deletions(-) create mode 100644 contracts/sui/xcall/sources/cluster_connection/cluster_connection.move create mode 100644 contracts/sui/xcall/sources/cluster_connection/cluster_entry.move create mode 100644 contracts/sui/xcall/sources/cluster_connection/cluster_state.move create mode 100644 contracts/sui/xcall/sources/cluster_connection/signatures.move diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move new file mode 100644 index 00000000..d1fa2dc4 --- /dev/null +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move @@ -0,0 +1,55 @@ +#[allow(unused_field,unused_use,unused_const,unused_mut_parameter,unused_variable,unused_assignment)] +module xcall::cluster_connection { + use xcall::centralized_state::{Self,State, ReceiptKey,get_state,get_state_mut}; + use std::string::{Self, String}; + use sui::bag::{Bag, Self}; + use sui::event; + use sui::table::{Self, Table}; + use sui::sui::SUI; + use sui::coin::{Self, Coin}; + use sui::balance; + use xcall::xcall_utils::{Self as utils}; + use xcall::xcall_state::{Self,ConnCap}; + use xcall::xcall_state::{Storage as XCallState}; + + const ENotEnoughFee: u64 = 10; + /* ================= events ================= */ + + public struct Message has copy, drop { + to:String, + conn_sn:u128, + msg:vector, + // same package can instantiate multiple connection so this is required + connection_id:String, + + } + + public(package) fun connect():State{ + centralized_state::create() + } + + public fun get_fee(states:&Bag,connection_id:String,netId:String,response:bool):u64{ + let state= get_state(states,connection_id); + centralized_state::get_fee(state,&netId,response) + } + + public(package) fun get_next_connection_sn(state:&mut State):u128 { + let sn = centralized_state::get_next_conn_sn(state); + sn + } + // this is safe because only package can call this other xcall will call other deployed instance + public(package) fun send_message(states:&mut Bag,connection_id:String,coin:Coin,to:String,sn:u128,msg:vector,is_response:bool,ctx: &mut TxContext){ + let mut fee = 0; + if(!is_response){ + fee = get_fee(states,connection_id, to, sn>0); + }; + assert!(coin.value() >= fee, ENotEnoughFee); + let balance = coin.into_balance(); + centralized_state::deposit(get_state_mut(states,connection_id),balance); + let conn_sn = get_next_connection_sn(get_state_mut(states,connection_id)); + + event::emit(Message { to, conn_sn, msg, connection_id }); + } + +} + diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move new file mode 100644 index 00000000..4013ae02 --- /dev/null +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move @@ -0,0 +1,66 @@ +module xcall::cluster_entry{ + + use xcall::main::{Self as xcall}; + use xcall::xcall_state::{Self,Storage as XCallState,ConnCap}; + use xcall::cluster_state::{Self,get_state,get_state_mut, Validator}; + use std::string::{String}; + + entry public fun receive_message(xcall:&mut XCallState,cap:&ConnCap,src_net_id:String,sn:u128,msg:vector,ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::check_save_receipt(state, src_net_id, sn); + xcall::handle_message(xcall, cap,src_net_id, msg,ctx); + } + + + entry fun claim_fees(xcall:&mut XCallState,cap:&ConnCap,ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::claim_fees(state,ctx); + } + + entry fun set_fee(xcall:&mut XCallState,cap:&ConnCap,net_id:String,message_fee:u64,response_fee:u64, ctx: &TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::set_fee(state,net_id,message_fee,response_fee,ctx.sender()); + } + + entry fun get_receipt(states: &XCallState,connection_id:String,net_id:String,sn:u128,_ctx: &TxContext):bool{ + let state = get_state(states.get_connection_states(),connection_id); + cluster_state::get_receipt(state,net_id,sn) + } + + entry fun get_fee(states: &XCallState,connection_id:String,net_id:String,response:bool,_ctx: &TxContext):u64{ + let state = get_state(states.get_connection_states(),connection_id); + cluster_state::get_fee(state,&net_id,response) + } + + entry fun add_validator(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:String,_ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::add_validator(state,validator_pubkey); + } + + entry fun remove_validator(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:String,ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::remove_validator(state,validator_pubkey,ctx); + } + + entry fun set_validator_threshold(xcall:&mut XCallState,cap:&ConnCap,threshold:u64,_ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::set_validator_threshold(state,threshold); + } + + entry fun get_validators(states: &XCallState,connection_id:String,_ctx: &TxContext):vector{ + let state = get_state(states.get_connection_states(),connection_id); + cluster_state::get_validators(state) + } + + entry fun get_validators_threshold(states: &XCallState,connection_id:String,_ctx: &TxContext):u64{ + let state = get_state(states.get_connection_states(),connection_id); + cluster_state::get_validator_threshold(state) + } + + entry fun recieve_message_with_signatures(xcall:&mut XCallState,cap:&ConnCap,src_net_id:String,sn:u128,msg:vector,signatures:vector>,ctx: &mut TxContext){ + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + cluster_state::verify_signatures(state, msg,signatures); + cluster_state::check_save_receipt(state, src_net_id, sn); + xcall::handle_message(xcall, cap,src_net_id, msg,ctx); + } +} \ No newline at end of file diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move new file mode 100644 index 00000000..0aab444c --- /dev/null +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move @@ -0,0 +1,273 @@ +module xcall::cluster_state { + use std::string::{String}; + use sui::vec_map::{Self, VecMap}; + use xcall::xcall_utils::{Self as utils}; + use xcall::signatures::{pubkey_to_sui_address, verify_signature, get_pubkey_from_signature}; + use sui::coin::{Self}; + use sui::balance::{Self, Balance}; + use sui::sui::SUI; + use sui::bag::{Bag, Self}; + use sui::event; + + //ERRORS + const VerifiedSignaturesLessThanThreshold: u64 = 100; + const NotEnoughSignatures: u64 = 101; + const InvalidThreshold: u64 = 102; + const RemovingValidatorNotInList: u64 = 103; + const AdminValidatorCannotBeRemoved: u64 = 104; + const ValidatorCountMustBeGreaterThanThreshold: u64 = 105; + const ValidatorAlreadyExists: u64 = 106; + + //EVENTS + public struct ValidatorAdded has copy, drop { + validator: address + } + + public struct ValidatorRemoved has copy, drop { + validator: address + } + + public(package) fun get_state_mut(states:&mut Bag,connection_id:String):&mut State { + let state:&mut State=bag::borrow_mut(states,connection_id); + state + } + + public fun get_state(states:&Bag,connection_id:String):&State { + let state:&State=bag::borrow(states,connection_id); + state + } + + + public struct ReceiptKey has copy, drop, store { + conn_sn: u128, + nid: String, + } + + public struct Validator has store, drop, copy{ + pub_key:vector, + sui_address:address, + } + + public struct State has store{ + message_fee: VecMap, + response_fee: VecMap, + receipts: VecMap, + conn_sn: u128, + balance: Balance, + validators: vector, + validators_threshold:u64, + + } + + public(package) fun create(): State { + State { + message_fee: vec_map::empty(), + response_fee: vec_map::empty(), + conn_sn: 0, + receipts: vec_map::empty(), + balance:balance::zero(), + validators: vector::empty(), + validators_threshold:0 + } + } + + fun get_validator(pub_key:String):Validator{ + let (pubkey,sui_address)=pubkey_to_sui_address(&pub_key); + Validator{ + pub_key:pubkey, + sui_address:sui_address + } + } + + public(package) fun get_next_conn_sn(self:&mut State):u128 { + let sn=self.conn_sn+1; + self.conn_sn=sn; + sn + } + + public fun get_fee(self: &State, netId: &String, need_response: bool): u64 { + let fee: u64 = if (need_response == true) { + utils::get_or_default(&self.message_fee, netId, 0) + + utils::get_or_default(&self.response_fee, netId, 0) + } else { + utils::get_or_default(&self.message_fee, netId, 0) + }; + fee + } + + public(package) fun set_fee(self: &mut State, net_id: String, message_fee: u64, response_fee: u64,caller:address) { + if (vec_map::contains(&self.message_fee,&net_id)){ + vec_map::remove(&mut self.message_fee,&net_id); + }; + if (vec_map::contains(&self.response_fee,&net_id)){ + vec_map::remove(&mut self.response_fee,&net_id); + }; + vec_map::insert(&mut self.message_fee, net_id, message_fee); + vec_map::insert(&mut self.response_fee, net_id, response_fee); + } + + public(package) fun check_save_receipt(self: &mut State, net_id: String, sn: u128) { + let receipt_key = ReceiptKey { nid: net_id, conn_sn: sn }; + assert!(!vec_map::contains(&self.receipts, &receipt_key), 100); + vec_map::insert(&mut self.receipts, receipt_key, true); + } + + public(package) fun get_receipt(self: &State, net_id: String, sn: u128): bool { + let receipt_key = ReceiptKey { nid: net_id, conn_sn: sn }; + vec_map::contains(&self.receipts, &receipt_key) + } + + public(package) fun deposit(self:&mut State,balance:Balance){ + balance::join(&mut self.balance,balance); + + } + + public(package) fun claim_fees(self:&mut State,ctx:&mut TxContext){ + let total= self.balance.withdraw_all(); + let coin= coin::from_balance(total,ctx); + transfer::public_transfer(coin,ctx.sender()); + + } + + public(package) fun verify_signatures(self:&State,msg:vector,signatures:vector>){ + let threshold=self.get_validator_threshold(); + let validators=self.get_validators().map!(|validator| validator.pub_key); + assert!(signatures.length() >= threshold, NotEnoughSignatures); + let mut total=0; + let mut i = 0; + while (i < signatures.length()) { + let signature = signatures.borrow(i); + let pub_key = get_pubkey_from_signature(signature); + if (validators.contains(&pub_key)) { + + if (verify_signature(&pub_key,signature,&msg)){ + total=total+1; + + if (total >= threshold) { + return + }; + }; + }; + i=i+1; + }; + assert!(total >= threshold, VerifiedSignaturesLessThanThreshold); + } + + public(package) fun get_validator_threshold(self:&State):u64{ + self.validators_threshold + } + + public(package) fun set_validator_threshold(self:&mut State,threshold:u64){ + assert!(threshold <= self.validators.length(), InvalidThreshold); + self.validators_threshold=threshold + } + + public(package) fun add_validator(self:&mut State,validator_pub_key:String){ + let validator=get_validator(validator_pub_key); + assert!(!self.validators.contains(&validator), ValidatorAlreadyExists); + self.validators.push_back(validator); + event::emit(ValidatorAdded { validator: validator.sui_address }); + } + + public(package) fun remove_validator(self:&mut State,validator_pub_key:String,ctx:&TxContext){ + assert!(self.validators.length() > self.validators_threshold, ValidatorCountMustBeGreaterThanThreshold); + let validator=get_validator(validator_pub_key); + let (contains, index) = self.validators.index_of(&validator); + assert!(contains, RemovingValidatorNotInList); + assert!(ctx.sender() != validator.sui_address, AdminValidatorCannotBeRemoved); + self.validators.remove(index); + + event::emit(ValidatorRemoved { validator: validator.sui_address }); + } + + public(package) fun get_validators(self:&State):vector{ + self.validators + } + + #[test_only] + public(package) fun create_state():State{ + State { + message_fee: vec_map::empty(), + response_fee: vec_map::empty(), + conn_sn: 0, + receipts: vec_map::empty(), + balance:balance::zero(), + validators: vector::empty(), + validators_threshold:0 + } + } + +} + +#[test_only] +module xcall::cluster_state_tests { + use xcall::cluster_state::{State, get_validators, get_validator_threshold, set_validator_threshold, add_validator, remove_validator, verify_signatures}; + use sui::test_scenario::{Self, Scenario}; + + #[test] + fun test_add_validator(): State { + let mut state = xcall::cluster_state::create_state(); + add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); + add_validator(&mut state, b"ADDxHCpQUcFsy5H5Gy01uv7LoISvtJLfgVGfWy4bLrjO".to_string()); + add_validator(&mut state, b"AL0hUNIiz5Q2fv0siZc75ce3aOyUpiiI+Q8Rmfay4K/X".to_string()); + add_validator(&mut state, b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lP/zuUDzBpJIA".to_string()); + + let validators = get_validators(&state); + assert!((validators.length() == 4)); + + assert!(get_validator_threshold(&state)==0); + + set_validator_threshold(&mut state, 2); + assert!(get_validator_threshold(&state)==2); + + state + } + + #[test] + #[expected_failure(abort_code = 106)] + fun test_add_repeated_validator(): State { + let mut state = test_add_validator(); + add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); + add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); + state + } + + #[test] + fun test_set_get_threshold(): State { + let mut state = test_add_validator(); + set_validator_threshold(&mut state, 2); + assert!(get_validator_threshold(&state)==2); + state + } + + #[test] + #[expected_failure(abort_code = 102)] + fun test_set_threshold_too_high(): State { + let mut state = test_set_get_threshold(); + set_validator_threshold(&mut state, 5); + state + } + + #[test] + #[expected_failure(abort_code = 103)] + fun test_remove_validator_not_in_list(): State { + let mut scenario = test_scenario::begin(@0xadd); + let mut state = test_set_get_threshold(); + remove_validator(&mut state, b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lS/zuUDzBpJIA".to_string(), scenario.ctx()); + test_scenario::end(scenario); + state + } + + #[test] + fun test_remove_validator(): State { + let mut scenario = test_scenario::begin(@0xadd); + let mut state = test_set_get_threshold(); + remove_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string(), scenario.ctx()); + test_scenario::end(scenario); + + let validators = get_validators(&state); + assert!((validators.length() == 3)); + state + } + +} \ No newline at end of file diff --git a/contracts/sui/xcall/sources/cluster_connection/signatures.move b/contracts/sui/xcall/sources/cluster_connection/signatures.move new file mode 100644 index 00000000..81fe18ad --- /dev/null +++ b/contracts/sui/xcall/sources/cluster_connection/signatures.move @@ -0,0 +1,196 @@ +#[allow(unused_field,unused_use,unused_const,unused_mut_parameter,unused_variable,unused_assignment)] + +module xcall::signatures { + use sui::linked_table::{Self, LinkedTable}; + use sui::types as sui_types; + use std::string::{Self, String}; + use sui::event; + use sui::hash::{Self}; + use sui::vec_map::{Self, VecMap}; + use sui::table::{Table,Self}; + use sui::bcs::{Self}; + use sui::address::{Self}; + use sui::{ed25519::ed25519_verify}; + use sui::{ecdsa_k1::secp256k1_verify}; + use sui::{ecdsa_r1::secp256r1_verify}; + + const BASE64_CHARS: vector = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const PADDING_CHAR: vector = b"="; + + /** signature schemes*/ + const FlagED25519 :u8= 0x00; + const FlagSecp256k1 :u8= 0x01; + const FlagSecp256r1 :u8= 0x02; + const FlagMultiSig :u8= 0x03; + + /* hash algorithm*/ + const KECCAK256: u8 = 0x00; + const SHA256: u8 = 0x01; + + public fun decode(input:&String):vector{ + let char_index=get_char_map(); + let mut output = vector::empty(); + let input_bytes = input.as_bytes(); + let mut i = 0; + while( i < input_bytes.length()){ + let b1 = *char_index.get(input_bytes.borrow(i)); + let b2 = *char_index.get(input_bytes.borrow(i + 1)); + let b3 = if (i + 2 < input_bytes.length()) { + let key=input_bytes.borrow(i+2); + let val:u32 = if (char_index.contains(key)) { + *char_index.get(key) + } else { + 64 + }; + val + } else { + 64 + }; + let b4 = if (i + 3 < input_bytes.length()) { + let key=input_bytes.borrow(i+3); + let val:u32= if (char_index.contains(key)) { + *char_index.get(key) + } else { + 64 + }; + val + } else { + 64 + }; + let triple = (b1 << 18) | (b2 << 12) | (b3 << 6) | b4; + output.push_back(((triple >> 16) & 0xFF) as u8); + if (b3 != 64) { + output.push_back(((triple >> 8) & 0xFF) as u8); + }; + if (b4 != 64) { + output.push_back((triple & 0xFF) as u8); + }; + i = i+4; + }; + output + } + + fun get_char_map():VecMap{ + let mut char_map = vec_map::empty(); + let mut i:u64=0; + while( i < BASE64_CHARS.length()){ + let c=*BASE64_CHARS.borrow(i); + char_map.insert(c,(i as u32)); + i=i+1; + }; + char_map + } + + public(package) fun pubkey_to_sui_address(pubkey:&String):(vector,address){ + let pubkey_bytes = decode(pubkey); + let sui_address = address::from_bytes(hash::blake2b256(&pubkey_bytes)); + (pubkey_bytes,sui_address) + } + + public(package) fun get_pubkey_from_signature(raw_signature:&vector):vector{ + let (signature,mut pubkey,scheme)= split_signature(raw_signature); + pubkey.insert(scheme, 0); + pubkey + } + + public(package) fun verify_signature(pubkey:&vector,raw_signature:&vector,msg:&vector):bool{ + let flag=*pubkey.borrow(0); + let public_key=slice_vector(pubkey,1,pubkey.length()-1); + let (signature,pub,_scheme)= split_signature(raw_signature); + + let intent_msg= get_intent_message(msg); + let digest= hash::blake2b256(&intent_msg); + + let verify= if(flag==FlagED25519){ + + ed25519_verify(&signature,&public_key,&digest) + + }else if(flag==FlagSecp256k1){ + + secp256k1_verify(&signature,&public_key,msg,SHA256) + + }else if (flag==FlagSecp256r1){ + + secp256r1_verify(&signature,&public_key,msg,SHA256) + }else { + return false + }; + verify + } + + fun get_intent_message(msg:&vector):vector{ + let mut intent_message:vector =vector::empty(); + intent_message.push_back(0x00); + intent_message.push_back(0x00); + intent_message.push_back(0x00); + intent_message.append(*msg); + intent_message + } + + fun split_signature(raw_signature:&vector):(vector,vector,u8){ + let scheme=*raw_signature.borrow(0); + let length= if (scheme==FlagED25519){32}else{33}; + let signature=slice_vector(raw_signature,1,64); + let pubkey=slice_vector(raw_signature,raw_signature.length()-length,length); + return (signature,pubkey,scheme) + } + + public fun slice_vector(vec: &vector, start: u64, length: u64): vector { + let mut result = vector::empty(); + let mut i = 0; + while (i < length) { + let value = *vector::borrow(vec, start + i); + vector::push_back(&mut result, value); + i = i + 1; + }; + result + } + +} + +#[test_only] +module xcall::signature_tests { + use sui::hash::{Self}; + use xcall::signatures::{decode, pubkey_to_sui_address, get_pubkey_from_signature, verify_signature}; + use std::debug::print; + use sui::{ed25519::ed25519_verify}; + + #[test] + fun test_base64(){ + let input = b"SGVsbG8sIFdvcmxkIQ==".to_string(); + let decoded = decode(&input); + assert!(decoded==b"Hello, World!",0x01); + } + + #[test] + fun test_pubkey_to_sui_address(){ + let input = b"AL0hUNIiz5Q2fv0siZc75ce3aOyUpiiI+Q8Rmfay4K/X".to_string(); + let (_pubkey_bytes,sui_address) = pubkey_to_sui_address(&input); + assert!(sui_address==@0xdefd91e5bfdb22edc6cabe1e9490549377047ec476354241910685876f1af34a,0x01); + } + + #[test] + fun test_get_pubkey_from_signature(){ + let input = b"ADQTOEZPWpPeZc9auXhwyciH7Pw6ny8xSxR+JVnSBnNktKAgTgxJ2EwHcErw55enK4w6c1uYaUv2gfY8Vb3X+Q6UHzNBcHIzSfMdKerK6XOCCmHbsI9Tpa8lGFvkm+Gbwg==".to_string(); + let pubkey = get_pubkey_from_signature(&decode(&input)); + assert!(pubkey==decode(&b"AJQfM0FwcjNJ8x0p6srpc4IKYduwj1OlryUYW+Sb4ZvC".to_string()),0x01); + } + + #[test] + fun test_verify_signature(){ + let data=x"6162636465666768"; + let pubkey = b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lP/zuUDzBpJIA".to_string(); + let signature = b"ALsKe6SiQqSYjIILlKjfmzEunnz0+DArU+4uBG522obq6cFSlqQhuN3bKcr6jVBSPgsEMAIW45PUXAc5oOq45gy5xu4WMO8+cRFEpkjbBruyKE9ydM++5T/87lA8waSSAA==".to_string(); + let verify = verify_signature(&decode(&pubkey),&decode(&signature),&data); + assert!(verify,0x01); + + let data=x"6162636465666768"; + let pubkey = x"b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200"; + let signature = x"c2ac48f07c55a8da3a0a762032a4956b08c63eebbe3f7a3f4cc56cd7fa23b1a3f7dec39e8e112162797d7c98f87d07314abff3cf594e42c27e942d3af43dcc07"; + + + let verify = ed25519_verify(&signature, &pubkey, &data); + assert!(verify,0x01); + + } +} diff --git a/contracts/sui/xcall/sources/connections.move b/contracts/sui/xcall/sources/connections.move index 170e2d0f..5174f7a5 100644 --- a/contracts/sui/xcall/sources/connections.move +++ b/contracts/sui/xcall/sources/connections.move @@ -1,9 +1,9 @@ -#[allow(unused_field,unused_use,unused_const,unused_mut_parameter,unused_variable,unused_assignment)] +#[allow(unused_field,unused_use,unused_const,unused_mut_parameter,unused_variable,unused_assignment,implicit_const_copy)] module xcall::connections{ use std::string::{Self, String}; use sui::bag::{Bag, Self}; use xcall::centralized_connection::{Self}; - use xcall::centralized_state::{Self,State}; + use xcall::cluster_connection::{Self}; use xcall::xcall_state::{ConnCap}; use sui::coin::{Self,Coin}; use sui::balance::{Self, Balance}; @@ -12,13 +12,18 @@ module xcall::connections{ const EConnectionNotFound:u64=0; const ConnCentralized:vector =b"centralized"; + const ConnCluster:vector =b"cluster"; public(package) fun register(states:&mut Bag,connection_id:String,ctx:&mut TxContext){ - if (get_connection_type(&connection_id).bytes()==ConnCentralized){ + if (get_connection_type(&connection_id).as_bytes()==ConnCentralized){ let state= centralized_connection::connect(); bag::add(states, connection_id, state); + }else + if (get_connection_type(&connection_id).as_bytes()==ConnCluster){ + let state= cluster_connection::connect(); + bag::add(states, connection_id, state); }else{ abort EConnectionNotFound } @@ -27,26 +32,33 @@ module xcall::connections{ public(package) fun get_fee(states:&Bag,connection_id:String,netId:String,response:bool):u64{ - if (get_connection_type(&connection_id).bytes()==ConnCentralized){ + if (get_connection_type(&connection_id).as_bytes()==ConnCentralized){ let fee= centralized_connection::get_fee(states,connection_id,netId,response); fee + }else + if (get_connection_type(&connection_id).as_bytes()==ConnCluster){ + let fee= cluster_connection::get_fee(states,connection_id,netId,response); + fee }else{ abort EConnectionNotFound - } + } } public(package) fun send_message(states:&mut Bag,connection_id:String,coin:Coin,netId:String,sn:u128,msg:vector,is_response:bool,ctx:&mut TxContext){ - if (get_connection_type(&connection_id).bytes()==ConnCentralized){ + if (get_connection_type(&connection_id).as_bytes()==ConnCentralized){ centralized_connection::send_message(states,connection_id,coin,netId,sn,msg,is_response,ctx); + }else + if (get_connection_type(&connection_id).as_bytes()==ConnCluster){ + cluster_connection::send_message(states,connection_id,coin,netId,sn,msg,is_response,ctx); }else{ abort EConnectionNotFound - } + } } fun get_connection_type(connection_id:&String):String{ let separator_index=string::index_of(connection_id,&string::utf8(b"-")); - let connType=string::sub_string(connection_id,0,separator_index); + let connType=string::substring(connection_id,0,separator_index); connType } From 0bc212a7893fd68e0a5e2e3068657df756c78cfc Mon Sep 17 00:00:00 2001 From: gcranju <075bct064.ranju@pcampus.edu.np> Date: Sun, 6 Oct 2024 18:38:45 +0545 Subject: [PATCH 2/5] feat: added tests for cluster state --- .../cluster_connection/cluster_state.move | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move index 0aab444c..8c1ac397 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move @@ -270,4 +270,84 @@ module xcall::cluster_state_tests { state } + #[test] + fun test_get_fee(): State { + let mut state = xcall::cluster_state::create_state(); + xcall::cluster_state::set_fee(&mut state, b"net1".to_string(), 100, 50, @0xadd); + xcall::cluster_state::set_fee(&mut state, b"net2".to_string(), 200, 100, @0xadd); + + let fee_without_response = xcall::cluster_state::get_fee(&state, &b"net1".to_string(), false); + assert!(fee_without_response == 100); + + let fee_with_response = xcall::cluster_state::get_fee(&state, &b"net1".to_string(), true); + assert!(fee_with_response == 150); + + state + } + + #[test] + fun test_update_fee(): State { + let mut state = xcall::cluster_state::create_state(); + xcall::cluster_state::set_fee(&mut state, b"net1".to_string(), 200, 100, @0xadd); + + let fee = xcall::cluster_state::get_fee(&state, &b"net1".to_string(), true); + assert!(fee == 300); // 200 message_fee + 100 response_fee + + // Update the fee + xcall::cluster_state::set_fee(&mut state, b"net1".to_string(), 300, 200, @0xadd); + let updated_fee = xcall::cluster_state::get_fee(&state, &b"net1".to_string(), true); + assert!(updated_fee == 500); // 300 message_fee + 200 response_fee + + state + } + + #[test] + fun test_receipts(): State { + let mut state = xcall::cluster_state::create_state(); + let sn = xcall::cluster_state::get_next_conn_sn(&mut state); + + xcall::cluster_state::check_save_receipt(&mut state, b"net1".to_string(), sn); + let receipt_exists = xcall::cluster_state::get_receipt(&state, b"net1".to_string(), sn); + assert!(receipt_exists == true); + + state + } + + #[test] + #[expected_failure(abort_code = 101)] + fun test_verify_signatures_less_than_threshold(): State { + let state = test_set_get_threshold(); + + let msg: vector = x"6162636465666768"; + let signatures = vector[x"00bb0a7ba4a242a4988c820b94a8df9b312e9e7cf4f8302b53ee2e046e76da86eae9c15296a421b8dddb29cafa8d50523e0b04300216e393d45c0739a0eab8e60cb9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200"]; + + xcall::cluster_state::verify_signatures(&state, msg, signatures); + state + } + + #[test] + #[expected_failure(abort_code = 100)] + fun test_verify_signatures_invalid(): State { + let state = test_set_get_threshold(); + let msg: vector = x"6162636465666768"; + let signatures = vector[x"00bb0a7ba4a242a4988c820b94a8df9b312e9e7cf4f8302b53ee2e046e76da86eae9c15296a421b8dddb29cafa8d50523e0b04300216e393d45c0739a0eab8e60cb9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200", + x"00c6d94cc625e73e036852316d228e578893aad2a7b21febc08f92a5d53978154782e4a0551ced23fb92c765b4cb4715e231de0235e2b641b81a36b9f2a3f8630d9eac9cd35a0e13d92e0d3a02472f60bd1365ee3e260a16b59286fc4266cdd5a2" + ]; + + xcall::cluster_state::verify_signatures(&state, msg, signatures); + state + } + + #[test] + fun test_verify_signatures(): State { + let state = test_set_get_threshold(); + let msg: vector = x"6162636465666768"; + let signatures = vector[x"00bb0a7ba4a242a4988c820b94a8df9b312e9e7cf4f8302b53ee2e046e76da86eae9c15296a421b8dddb29cafa8d50523e0b04300216e393d45c0739a0eab8e60cb9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200", + x"00c6d94cc625e73e036852316d229e578893aad2a7b21febc08f92a5d53978154782e4a0551ced23fb92c765b4cb4715e231de0235e2b641b81a36b9f2a3f8630d9eac9cd35a0e13d92e0d3a02472f60bd1365ee3e260a16b59286fc4266cdd5a2" + ]; + + xcall::cluster_state::verify_signatures(&state, msg, signatures); + state + } + } \ No newline at end of file From 441f1d8ba277f798b77950240effa19e8380889b Mon Sep 17 00:00:00 2001 From: Itshyphen <075bct064.ranju@pcampus.edu.np> Date: Tue, 8 Oct 2024 21:15:57 +0545 Subject: [PATCH 3/5] fix: unique pubkeys verification --- .../sources/cluster_connection/cluster_state.move | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move index 8c1ac397..835f7137 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move @@ -133,24 +133,27 @@ module xcall::cluster_state { let threshold=self.get_validator_threshold(); let validators=self.get_validators().map!(|validator| validator.pub_key); assert!(signatures.length() >= threshold, NotEnoughSignatures); - let mut total=0; let mut i = 0; + let mut unique_verified_pubkey = vector::empty(); while (i < signatures.length()) { let signature = signatures.borrow(i); let pub_key = get_pubkey_from_signature(signature); if (validators.contains(&pub_key)) { if (verify_signature(&pub_key,signature,&msg)){ - total=total+1; - if (total >= threshold) { + if (!unique_verified_pubkey.contains(&pub_key)){ + unique_verified_pubkey.push_back(pub_key); + }; + + if (unique_verified_pubkey.length() >= threshold) { return }; }; }; i=i+1; }; - assert!(total >= threshold, VerifiedSignaturesLessThanThreshold); + assert!(unique_verified_pubkey.length() >= threshold, VerifiedSignaturesLessThanThreshold); } public(package) fun get_validator_threshold(self:&State):u64{ From db50e1f150c56c5251dedb955b78759aad5acec4 Mon Sep 17 00:00:00 2001 From: Itshyphen <075bct064.ranju@pcampus.edu.np> Date: Fri, 18 Oct 2024 10:39:38 +0545 Subject: [PATCH 4/5] fix: sui-cluster-connection-spec-change --- .../cluster_connection.move | 10 +- .../cluster_connection/cluster_entry.move | 13 +-- .../cluster_connection/cluster_state.move | 103 +++++++----------- contracts/sui/xcall/sources/connections.move | 4 + contracts/sui/xcall/sources/utils.move | 13 ++- 5 files changed, 66 insertions(+), 77 deletions(-) diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move index d1fa2dc4..63f8bfbd 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move @@ -1,6 +1,6 @@ #[allow(unused_field,unused_use,unused_const,unused_mut_parameter,unused_variable,unused_assignment)] module xcall::cluster_connection { - use xcall::centralized_state::{Self,State, ReceiptKey,get_state,get_state_mut}; + use xcall::cluster_state::{Self,State, ReceiptKey,get_state,get_state_mut}; use std::string::{Self, String}; use sui::bag::{Bag, Self}; use sui::event; @@ -25,16 +25,16 @@ module xcall::cluster_connection { } public(package) fun connect():State{ - centralized_state::create() + cluster_state::create() } public fun get_fee(states:&Bag,connection_id:String,netId:String,response:bool):u64{ let state= get_state(states,connection_id); - centralized_state::get_fee(state,&netId,response) + cluster_state::get_fee(state,&netId,response) } public(package) fun get_next_connection_sn(state:&mut State):u128 { - let sn = centralized_state::get_next_conn_sn(state); + let sn = cluster_state::get_next_conn_sn(state); sn } // this is safe because only package can call this other xcall will call other deployed instance @@ -45,7 +45,7 @@ module xcall::cluster_connection { }; assert!(coin.value() >= fee, ENotEnoughFee); let balance = coin.into_balance(); - centralized_state::deposit(get_state_mut(states,connection_id),balance); + cluster_state::deposit(get_state_mut(states,connection_id),balance); let conn_sn = get_next_connection_sn(get_state_mut(states,connection_id)); event::emit(Message { to, conn_sn, msg, connection_id }); diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move index 4013ae02..60eb456d 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move @@ -3,6 +3,7 @@ module xcall::cluster_entry{ use xcall::main::{Self as xcall}; use xcall::xcall_state::{Self,Storage as XCallState,ConnCap}; use xcall::cluster_state::{Self,get_state,get_state_mut, Validator}; + use xcall::xcall_utils::{Self as utils}; use std::string::{String}; entry public fun receive_message(xcall:&mut XCallState,cap:&ConnCap,src_net_id:String,sn:u128,msg:vector,ctx: &mut TxContext){ @@ -32,14 +33,9 @@ module xcall::cluster_entry{ cluster_state::get_fee(state,&net_id,response) } - entry fun add_validator(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:String,_ctx: &mut TxContext){ + entry fun set_validators(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:vector,threshold:u64,_ctx: &mut TxContext){ let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); - cluster_state::add_validator(state,validator_pubkey); - } - - entry fun remove_validator(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:String,ctx: &mut TxContext){ - let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); - cluster_state::remove_validator(state,validator_pubkey,ctx); + cluster_state::set_validators(state,validator_pubkey,threshold); } entry fun set_validator_threshold(xcall:&mut XCallState,cap:&ConnCap,threshold:u64,_ctx: &mut TxContext){ @@ -59,7 +55,8 @@ module xcall::cluster_entry{ entry fun recieve_message_with_signatures(xcall:&mut XCallState,cap:&ConnCap,src_net_id:String,sn:u128,msg:vector,signatures:vector>,ctx: &mut TxContext){ let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); - cluster_state::verify_signatures(state, msg,signatures); + let message_hash = utils::get_message_hash(src_net_id, sn, msg); + cluster_state::verify_signatures(state, message_hash, signatures); cluster_state::check_save_receipt(state, src_net_id, sn); xcall::handle_message(xcall, cap,src_net_id, msg,ctx); } diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move index 8c1ac397..997ce1e2 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move @@ -13,18 +13,23 @@ module xcall::cluster_state { const VerifiedSignaturesLessThanThreshold: u64 = 100; const NotEnoughSignatures: u64 = 101; const InvalidThreshold: u64 = 102; - const RemovingValidatorNotInList: u64 = 103; - const AdminValidatorCannotBeRemoved: u64 = 104; const ValidatorCountMustBeGreaterThanThreshold: u64 = 105; - const ValidatorAlreadyExists: u64 = 106; //EVENTS - public struct ValidatorAdded has copy, drop { - validator: address + public struct ValidatorSetAdded has copy, drop { + validators: vector, + threshold: u64 + } + + public struct AdminCap has key,store { + id: UID } - public struct ValidatorRemoved has copy, drop { - validator: address + public(package) fun create_admin_cap(ctx: &mut TxContext):AdminCap { + let admin = AdminCap { + id: object::new(ctx), + }; + admin } public(package) fun get_state_mut(states:&mut Bag,connection_id:String):&mut State { @@ -129,7 +134,7 @@ module xcall::cluster_state { } - public(package) fun verify_signatures(self:&State,msg:vector,signatures:vector>){ + public(package) fun verify_signatures(self:&State,message_hash:vector,signatures:vector>){ let threshold=self.get_validator_threshold(); let validators=self.get_validators().map!(|validator| validator.pub_key); assert!(signatures.length() >= threshold, NotEnoughSignatures); @@ -140,7 +145,7 @@ module xcall::cluster_state { let pub_key = get_pubkey_from_signature(signature); if (validators.contains(&pub_key)) { - if (verify_signature(&pub_key,signature,&msg)){ + if (verify_signature(&pub_key,signature,&message_hash)){ total=total+1; if (total >= threshold) { @@ -162,22 +167,20 @@ module xcall::cluster_state { self.validators_threshold=threshold } - public(package) fun add_validator(self:&mut State,validator_pub_key:String){ - let validator=get_validator(validator_pub_key); - assert!(!self.validators.contains(&validator), ValidatorAlreadyExists); - self.validators.push_back(validator); - event::emit(ValidatorAdded { validator: validator.sui_address }); - } - - public(package) fun remove_validator(self:&mut State,validator_pub_key:String,ctx:&TxContext){ - assert!(self.validators.length() > self.validators_threshold, ValidatorCountMustBeGreaterThanThreshold); - let validator=get_validator(validator_pub_key); - let (contains, index) = self.validators.index_of(&validator); - assert!(contains, RemovingValidatorNotInList); - assert!(ctx.sender() != validator.sui_address, AdminValidatorCannotBeRemoved); - self.validators.remove(index); - - event::emit(ValidatorRemoved { validator: validator.sui_address }); + public(package) fun set_validators(self:&mut State,validator_pub_keys:vector,threshold:u64){ + self.validators=vector::empty(); + let mut validator_pub_keys = validator_pub_keys; + while (validator_pub_keys.length() > 0) { + let validator_pub_key = validator_pub_keys.pop_back(); + let validator=get_validator(validator_pub_key); + if(self.validators.contains(&validator)){ + continue + }; + self.validators.push_back(validator); + }; + assert!(self.validators.length() >= threshold, ValidatorCountMustBeGreaterThanThreshold); + self.validators_threshold=threshold; + event::emit(ValidatorSetAdded { validators: self.validators, threshold }); } public(package) fun get_validators(self:&State):vector{ @@ -201,36 +204,32 @@ module xcall::cluster_state { #[test_only] module xcall::cluster_state_tests { - use xcall::cluster_state::{State, get_validators, get_validator_threshold, set_validator_threshold, add_validator, remove_validator, verify_signatures}; + use xcall::cluster_state::{State, get_validators, get_validator_threshold, set_validator_threshold, set_validators, verify_signatures}; use sui::test_scenario::{Self, Scenario}; #[test] fun test_add_validator(): State { let mut state = xcall::cluster_state::create_state(); - add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); - add_validator(&mut state, b"ADDxHCpQUcFsy5H5Gy01uv7LoISvtJLfgVGfWy4bLrjO".to_string()); - add_validator(&mut state, b"AL0hUNIiz5Q2fv0siZc75ce3aOyUpiiI+Q8Rmfay4K/X".to_string()); - add_validator(&mut state, b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lP/zuUDzBpJIA".to_string()); + + let validators = vector[ + b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string(), + b"ADDxHCpQUcFsy5H5Gy01uv7LoISvtJLfgVGfWy4bLrjO".to_string(), + b"AL0hUNIiz5Q2fv0siZc75ce3aOyUpiiI+Q8Rmfay4K/X".to_string(), + b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lP/zuUDzBpJIA".to_string() + ]; + + set_validators(&mut state, validators, 2); let validators = get_validators(&state); assert!((validators.length() == 4)); - assert!(get_validator_threshold(&state)==0); - set_validator_threshold(&mut state, 2); - assert!(get_validator_threshold(&state)==2); + set_validator_threshold(&mut state, 3); + assert!(get_validator_threshold(&state)==3); state } - #[test] - #[expected_failure(abort_code = 106)] - fun test_add_repeated_validator(): State { - let mut state = test_add_validator(); - add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); - add_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string()); - state - } #[test] fun test_set_get_threshold(): State { @@ -248,28 +247,6 @@ module xcall::cluster_state_tests { state } - #[test] - #[expected_failure(abort_code = 103)] - fun test_remove_validator_not_in_list(): State { - let mut scenario = test_scenario::begin(@0xadd); - let mut state = test_set_get_threshold(); - remove_validator(&mut state, b"ALnG7hYw7z5xEUSmSNsGu7IoT3J0z77lS/zuUDzBpJIA".to_string(), scenario.ctx()); - test_scenario::end(scenario); - state - } - - #[test] - fun test_remove_validator(): State { - let mut scenario = test_scenario::begin(@0xadd); - let mut state = test_set_get_threshold(); - remove_validator(&mut state, b"AJ6snNNaDhPZLg06AkcvYL0TZe4+JgoWtZKG/EJmzdWi".to_string(), scenario.ctx()); - test_scenario::end(scenario); - - let validators = get_validators(&state); - assert!((validators.length() == 3)); - state - } - #[test] fun test_get_fee(): State { let mut state = xcall::cluster_state::create_state(); diff --git a/contracts/sui/xcall/sources/connections.move b/contracts/sui/xcall/sources/connections.move index 5174f7a5..d2c09098 100644 --- a/contracts/sui/xcall/sources/connections.move +++ b/contracts/sui/xcall/sources/connections.move @@ -4,10 +4,12 @@ module xcall::connections{ use sui::bag::{Bag, Self}; use xcall::centralized_connection::{Self}; use xcall::cluster_connection::{Self}; + use xcall::cluster_state::{Self,State,create_admin_cap}; use xcall::xcall_state::{ConnCap}; use sui::coin::{Self,Coin}; use sui::balance::{Self, Balance}; use sui::sui::SUI; + const EConnectionNotFound:u64=0; @@ -23,6 +25,8 @@ module xcall::connections{ }else if (get_connection_type(&connection_id).as_bytes()==ConnCluster){ let state= cluster_connection::connect(); + let admin_cap=cluster_state::create_admin_cap(ctx); + transfer::public_transfer(admin_cap, ctx.sender()); bag::add(states, connection_id, state); }else{ abort EConnectionNotFound diff --git a/contracts/sui/xcall/sources/utils.move b/contracts/sui/xcall/sources/utils.move index 66e0ff9d..889691b1 100644 --- a/contracts/sui/xcall/sources/utils.move +++ b/contracts/sui/xcall/sources/utils.move @@ -8,7 +8,9 @@ module xcall::xcall_utils { use std::string::{Self, String}; use sui::hex; use sui::bcs::{Self}; - + use sui_rlp::encoder::{Self}; + use sui::hash::{Self}; + public fun are_equal(a1:&vector,a2:&vector): bool { if(length(a1)!=length(a2)){ @@ -24,6 +26,15 @@ module xcall::xcall_utils { } } + public fun get_message_hash(src_net_id: String, sn: u128, msg: vector): vector { + let mut list=vector::empty>(); + vector::push_back(&mut list, encoder::encode_string(&src_net_id)); + vector::push_back(&mut list, encoder::encode_u128(sn)); + vector::push_back(&mut list, encoder::encode(&msg)); + let encoded=encoder::encode_list(&list,false); + hash::keccak256(&encoded) + + } public fun id_to_hex_string(id:&ID): String { let bytes = object::id_to_bytes(id); From 0dd5ec3037ba717e37cd6bfc51dc0f51f60b7ebc Mon Sep 17 00:00:00 2001 From: Itshyphen <075bct064.ranju@pcampus.edu.np> Date: Fri, 18 Oct 2024 18:58:46 +0545 Subject: [PATCH 5/5] fix: cluster connection admin cap --- .../cluster_connection/cluster_connection.move | 1 - .../cluster_connection/cluster_entry.move | 12 +++++++----- .../cluster_connection/cluster_state.move | 18 ++++++++++-------- contracts/sui/xcall/sources/connections.move | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move index 63f8bfbd..0f72f53a 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_connection.move @@ -21,7 +21,6 @@ module xcall::cluster_connection { msg:vector, // same package can instantiate multiple connection so this is required connection_id:String, - } public(package) fun connect():State{ diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move index 60eb456d..0bbf2fdc 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_entry.move @@ -2,7 +2,7 @@ module xcall::cluster_entry{ use xcall::main::{Self as xcall}; use xcall::xcall_state::{Self,Storage as XCallState,ConnCap}; - use xcall::cluster_state::{Self,get_state,get_state_mut, Validator}; + use xcall::cluster_state::{Self,get_state,get_state_mut,validate_admin_cap, Validator, AdminCap}; use xcall::xcall_utils::{Self as utils}; use std::string::{String}; @@ -33,13 +33,15 @@ module xcall::cluster_entry{ cluster_state::get_fee(state,&net_id,response) } - entry fun set_validators(xcall:&mut XCallState,cap:&ConnCap,validator_pubkey:vector,threshold:u64,_ctx: &mut TxContext){ - let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + entry fun set_validators(xcall:&mut XCallState,cap:&AdminCap,connection_id:String,validator_pubkey:vector,threshold:u64,_ctx: &mut TxContext){ + validate_admin_cap(cap,connection_id); + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),connection_id); cluster_state::set_validators(state,validator_pubkey,threshold); } - entry fun set_validator_threshold(xcall:&mut XCallState,cap:&ConnCap,threshold:u64,_ctx: &mut TxContext){ - let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),cap.connection_id()); + entry fun set_validator_threshold(xcall:&mut XCallState,cap:&AdminCap,connection_id:String,threshold:u64,_ctx: &mut TxContext){ + validate_admin_cap(cap,connection_id); + let state=get_state_mut(xcall_state::get_connection_states_mut(xcall),connection_id); cluster_state::set_validator_threshold(state,threshold); } diff --git a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move index 23eb3a56..5a0b7dcb 100644 --- a/contracts/sui/xcall/sources/cluster_connection/cluster_state.move +++ b/contracts/sui/xcall/sources/cluster_connection/cluster_state.move @@ -14,6 +14,7 @@ module xcall::cluster_state { const NotEnoughSignatures: u64 = 101; const InvalidThreshold: u64 = 102; const ValidatorCountMustBeGreaterThanThreshold: u64 = 105; + const InvalidAdminCap: u64 = 106; //EVENTS public struct ValidatorSetAdded has copy, drop { @@ -22,16 +23,22 @@ module xcall::cluster_state { } public struct AdminCap has key,store { - id: UID + id: UID, + connection_id: String } - public(package) fun create_admin_cap(ctx: &mut TxContext):AdminCap { + public(package) fun create_admin_cap(connection_id:String,ctx: &mut TxContext):AdminCap { let admin = AdminCap { id: object::new(ctx), + connection_id: connection_id }; admin } + public(package) fun validate_admin_cap(self:&AdminCap,connection_id:String){ + assert!(self.connection_id == connection_id, InvalidAdminCap); + } + public(package) fun get_state_mut(states:&mut Bag,connection_id:String):&mut State { let state:&mut State=bag::borrow_mut(states,connection_id); state @@ -145,12 +152,7 @@ module xcall::cluster_state { let pub_key = get_pubkey_from_signature(signature); if (validators.contains(&pub_key)) { -<<<<<<< HEAD - if (verify_signature(&pub_key,signature,&message_hash)){ - total=total+1; -======= - if (verify_signature(&pub_key,signature,&msg)){ ->>>>>>> 441f1d8ba277f798b77950240effa19e8380889b + if (verify_signature(&pub_key,signature,&message_hash)) { if (!unique_verified_pubkey.contains(&pub_key)){ unique_verified_pubkey.push_back(pub_key); diff --git a/contracts/sui/xcall/sources/connections.move b/contracts/sui/xcall/sources/connections.move index d2c09098..044ffddc 100644 --- a/contracts/sui/xcall/sources/connections.move +++ b/contracts/sui/xcall/sources/connections.move @@ -25,7 +25,7 @@ module xcall::connections{ }else if (get_connection_type(&connection_id).as_bytes()==ConnCluster){ let state= cluster_connection::connect(); - let admin_cap=cluster_state::create_admin_cap(ctx); + let admin_cap=cluster_state::create_admin_cap(connection_id,ctx); transfer::public_transfer(admin_cap, ctx.sender()); bag::add(states, connection_id, state); }else{