diff --git a/contracts/soroban/contracts/centralized-connection/src/contract.rs b/contracts/soroban/contracts/centralized-connection/src/contract.rs index 5dcd9913..507f5451 100644 --- a/contracts/soroban/contracts/centralized-connection/src/contract.rs +++ b/contracts/soroban/contracts/centralized-connection/src/contract.rs @@ -14,6 +14,7 @@ impl CentralizedConnection { storage::store_conn_sn(&env, 0); storage::store_admin(&env, msg.relayer); storage::store_xcall(&env, msg.xcall_address); + storage::store_upgrade_authority(&env, msg.upgrade_authority); Ok(()) } @@ -29,6 +30,18 @@ impl CentralizedConnection { 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 send_message( env: Env, tx_origin: Address, @@ -109,7 +122,7 @@ impl CentralizedConnection { } pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> { - helpers::ensure_admin(&env)?; + helpers::ensure_upgrade_authority(&env)?; env.deployer().update_current_contract_wasm(new_wasm_hash); Ok(()) diff --git a/contracts/soroban/contracts/centralized-connection/src/helpers.rs b/contracts/soroban/contracts/centralized-connection/src/helpers.rs index f702898e..d43fe65a 100644 --- a/contracts/soroban/contracts/centralized-connection/src/helpers.rs +++ b/contracts/soroban/contracts/centralized-connection/src/helpers.rs @@ -9,6 +9,13 @@ pub fn ensure_admin(e: &Env) -> Result { 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(); diff --git a/contracts/soroban/contracts/centralized-connection/src/storage.rs b/contracts/soroban/contracts/centralized-connection/src/storage.rs index 3bed6670..4a410aa0 100644 --- a/contracts/soroban/contracts/centralized-connection/src/storage.rs +++ b/contracts/soroban/contracts/centralized-connection/src/storage.rs @@ -29,6 +29,13 @@ pub fn admin(e: &Env) -> Result { .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() @@ -106,6 +113,12 @@ 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); } diff --git a/contracts/soroban/contracts/centralized-connection/src/test.rs b/contracts/soroban/contracts/centralized-connection/src/test.rs index 7d732f8c..a44bd217 100644 --- a/contracts/soroban/contracts/centralized-connection/src/test.rs +++ b/contracts/soroban/contracts/centralized-connection/src/test.rs @@ -26,6 +26,7 @@ pub struct TestContext { native_token: Address, token_admin: Address, nid: String, + upgrade_authority: Address, } impl TestContext { @@ -38,6 +39,7 @@ impl TestContext { relayer: Address::generate(&env), native_token: env.register_stellar_asset_contract(token_admin.clone()), nid: String::from_str(&env, "icon"), + upgrade_authority: Address::generate(&env), env, token_admin, } @@ -50,6 +52,7 @@ impl TestContext { relayer: self.relayer.clone(), native_token: self.native_token.clone(), xcall_address: self.xcall.clone(), + upgrade_authority: self.upgrade_authority.clone(), }); } @@ -66,6 +69,7 @@ fn get_dummy_initialize_msg(env: &Env) -> InitializeMsg { relayer: Address::generate(&env), native_token: env.register_stellar_asset_contract(Address::generate(&env)), xcall_address: Address::generate(&env), + upgrade_authority: Address::generate(&env), } } @@ -143,6 +147,34 @@ fn test_set_admin_fail() { ) } +#[test] +fn test_set_upgrade_authority() { + let ctx = TestContext::default(); + let client = CentralizedConnectionClient::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(); diff --git a/contracts/soroban/contracts/centralized-connection/src/types.rs b/contracts/soroban/contracts/centralized-connection/src/types.rs index fb275b75..e3f08bdb 100644 --- a/contracts/soroban/contracts/centralized-connection/src/types.rs +++ b/contracts/soroban/contracts/centralized-connection/src/types.rs @@ -5,6 +5,7 @@ use soroban_sdk::{contracttype, Address, String}; pub enum StorageKey { Xcall, Admin, + UpgradeAuthority, Xlm, ConnSn, NetworkFee(String), @@ -16,6 +17,7 @@ pub struct InitializeMsg { pub relayer: Address, pub native_token: Address, pub xcall_address: Address, + pub upgrade_authority: Address, } #[contracttype] diff --git a/contracts/soroban/contracts/xcall/src/contract.rs b/contracts/soroban/contracts/xcall/src/contract.rs index 49ab319b..47c2eec9 100644 --- a/contracts/soroban/contracts/xcall/src/contract.rs +++ b/contracts/soroban/contracts/xcall/src/contract.rs @@ -17,6 +17,7 @@ impl Xcall { storage::store_admin(&env, &msg.sender); storage::store_fee_handler(&env, &msg.sender); + storage::store_upgrade_authority(&env, &msg.upgrade_authority); storage::store_config( &env, Config { @@ -35,6 +36,13 @@ impl Xcall { Ok(()) } + 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_protocol_fee(env: &Env, fee: u128) -> Result<(), ContractError> { helpers::ensure_admin(&env)?; storage::store_protocol_fee(&env, fee); @@ -101,6 +109,11 @@ impl Xcall { Ok(admin) } + pub fn get_upgrade_authority(env: Env) -> Result { + let address = storage::get_upgrade_authority(&env)?; + Ok(address) + } + pub fn get_network_address(env: Env) -> Result { let network_address = storage::get_own_network_address(&env)?; Ok(network_address.to_string()) @@ -137,7 +150,7 @@ impl Xcall { } pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> { - helpers::ensure_admin(&env)?; + helpers::ensure_upgrade_authority(&env)?; env.deployer().update_current_contract_wasm(new_wasm_hash); Ok(()) diff --git a/contracts/soroban/contracts/xcall/src/helpers.rs b/contracts/soroban/contracts/xcall/src/helpers.rs index e9999920..5c6ff75d 100644 --- a/contracts/soroban/contracts/xcall/src/helpers.rs +++ b/contracts/soroban/contracts/xcall/src/helpers.rs @@ -17,6 +17,13 @@ pub fn ensure_admin(e: &Env) -> Result { 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_fee_handler(e: &Env) -> Result { let fee_handler = storage::get_fee_handler(&e)?; fee_handler.require_auth(); diff --git a/contracts/soroban/contracts/xcall/src/storage.rs b/contracts/soroban/contracts/xcall/src/storage.rs index d0f40b56..5deef0c5 100644 --- a/contracts/soroban/contracts/xcall/src/storage.rs +++ b/contracts/soroban/contracts/xcall/src/storage.rs @@ -54,6 +54,13 @@ pub fn get_fee_handler(e: &Env) -> Result { .ok_or(ContractError::Uninitialized) } +pub fn get_upgrade_authority(e: &Env) -> Result { + e.storage() + .instance() + .get(&StorageKey::UpgradeAuthority) + .ok_or(ContractError::Uninitialized) +} + pub fn protocol_fee(e: &Env) -> u128 { e.storage() .instance() @@ -162,6 +169,13 @@ pub fn store_fee_handler(e: &Env, address: &Address) { extend_instance(e) } +pub fn store_upgrade_authority(e: &Env, address: &Address) { + e.storage() + .instance() + .set(&StorageKey::UpgradeAuthority, &address); + extend_instance(e) +} + pub fn store_protocol_fee(e: &Env, fee: u128) { e.storage().instance().set(&StorageKey::ProtocolFee, &fee); extend_instance(e) diff --git a/contracts/soroban/contracts/xcall/src/test/contract.rs b/contracts/soroban/contracts/xcall/src/test/contract.rs index d73edf92..e7a4d06f 100644 --- a/contracts/soroban/contracts/xcall/src/test/contract.rs +++ b/contracts/soroban/contracts/xcall/src/test/contract.rs @@ -155,3 +155,31 @@ fn test_get_fee() { let fee = client.get_fee(&ctx.nid, &need_response, &Some(sources)); assert_eq!(fee, protocol_fee + centralized_conn_fee) } + +#[test] +fn test_set_upgrade_authority() { + let ctx = TestContext::default(); + let client = XcallClient::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); +} diff --git a/contracts/soroban/contracts/xcall/src/test/setup.rs b/contracts/soroban/contracts/xcall/src/test/setup.rs index 546b9096..e4591430 100644 --- a/contracts/soroban/contracts/xcall/src/test/setup.rs +++ b/contracts/soroban/contracts/xcall/src/test/setup.rs @@ -114,6 +114,7 @@ pub struct TestContext { pub native_token: Address, pub token_admin: Address, pub network_address: NetworkAddress, + pub upgrade_authority: Address, pub centralized_connection: Address, } @@ -130,6 +131,7 @@ impl TestContext { native_token: env.register_stellar_asset_contract(token_admin.clone()), nid: String::from_str(&env, "stellar"), network_address: get_dummy_network_address(&env), + upgrade_authority: Address::generate(&env), env, token_admin, centralized_connection, @@ -143,6 +145,7 @@ impl TestContext { sender: self.admin.clone(), network_id: String::from_str(&self.env, "icon"), native_token: self.native_token.clone(), + upgrade_authority: self.upgrade_authority.clone(), }); self.init_connection_state(); diff --git a/contracts/soroban/contracts/xcall/src/types/message.rs b/contracts/soroban/contracts/xcall/src/types/message.rs index 5b23dfe8..de608c95 100644 --- a/contracts/soroban/contracts/xcall/src/types/message.rs +++ b/contracts/soroban/contracts/xcall/src/types/message.rs @@ -91,4 +91,5 @@ pub struct InitializeMsg { pub network_id: String, pub sender: Address, pub native_token: Address, + pub upgrade_authority: Address, } diff --git a/contracts/soroban/contracts/xcall/src/types/storage_types.rs b/contracts/soroban/contracts/xcall/src/types/storage_types.rs index 904c44ac..f24d82e7 100644 --- a/contracts/soroban/contracts/xcall/src/types/storage_types.rs +++ b/contracts/soroban/contracts/xcall/src/types/storage_types.rs @@ -14,6 +14,7 @@ pub enum StorageKey { PendingRequests(BytesN<32>), PendingResponses(BytesN<32>), LastReqId, + UpgradeAuthority, } #[contracttype]