diff --git a/Cargo.lock b/Cargo.lock index b61d4215..acddec2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "multiversx-sc-scenario", "num-bigint", "proxies", + "setup-phase", "transaction", ] diff --git a/common/proxies/src/header_verifier_proxy.rs b/common/proxies/src/header_verifier_proxy.rs index 9de9ca01..01a7b046 100644 --- a/common/proxies/src/header_verifier_proxy.rs +++ b/common/proxies/src/header_verifier_proxy.rs @@ -148,4 +148,13 @@ where .argument(&operation_hash) .original_result() } + + pub fn complete_setup_phase( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("completeSetupPhase") + .original_result() + } } diff --git a/common/proxies/src/sovereign_forge_proxy.rs b/common/proxies/src/sovereign_forge_proxy.rs index 0f2fdd3c..46802e76 100644 --- a/common/proxies/src/sovereign_forge_proxy.rs +++ b/common/proxies/src/sovereign_forge_proxy.rs @@ -147,12 +147,16 @@ where .original_result() } - pub fn deploy_phase_two( + pub fn deploy_phase_two< + Arg0: ProxyArg>>, + >( self, + bls_keys: Arg0, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("deployPhaseTwo") + .argument(&bls_keys) .original_result() } diff --git a/enshrine-esdt-safe/interactor/src/config.rs b/enshrine-esdt-safe/interactor/src/config.rs index 4bf7c7d1..2d072b4b 100644 --- a/enshrine-esdt-safe/interactor/src/config.rs +++ b/enshrine-esdt-safe/interactor/src/config.rs @@ -3,25 +3,22 @@ use serde::Deserialize; use std::io::Read; - /// Config file const CONFIG_FILE: &str = "config.toml"; - #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ChainType { Real, Simulator, - } +} /// Contract Interact configuration #[derive(Debug, Deserialize)] pub struct Config { pub gateway_uri: String, pub chain_type: ChainType, - } - +} impl Config { // Deserializes config from file @@ -36,7 +33,7 @@ impl Config { Config { gateway_uri: "http://localhost:8085".to_owned(), chain_type: ChainType::Simulator, - } + } } // Returns the gateway URI @@ -49,8 +46,6 @@ impl Config { match self.chain_type { ChainType::Real => false, ChainType::Simulator => true, + } } - } - } - - +} diff --git a/enshrine-esdt-safe/wasm-enshrine-esdt-safe-full/Cargo.lock b/enshrine-esdt-safe/wasm-enshrine-esdt-safe-full/Cargo.lock index 26827edb..9c9627c2 100644 --- a/enshrine-esdt-safe/wasm-enshrine-esdt-safe-full/Cargo.lock +++ b/enshrine-esdt-safe/wasm-enshrine-esdt-safe-full/Cargo.lock @@ -90,6 +90,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/enshrine-esdt-safe/wasm-enshrine-esdt-safe-view/Cargo.lock b/enshrine-esdt-safe/wasm-enshrine-esdt-safe-view/Cargo.lock index 0e620be8..d7375f07 100644 --- a/enshrine-esdt-safe/wasm-enshrine-esdt-safe-view/Cargo.lock +++ b/enshrine-esdt-safe/wasm-enshrine-esdt-safe-view/Cargo.lock @@ -90,6 +90,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/enshrine-esdt-safe/wasm/Cargo.lock b/enshrine-esdt-safe/wasm/Cargo.lock index 92badb6c..4412ebd7 100644 --- a/enshrine-esdt-safe/wasm/Cargo.lock +++ b/enshrine-esdt-safe/wasm/Cargo.lock @@ -90,6 +90,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/esdt-safe/wasm-esdt-safe-full/Cargo.lock b/esdt-safe/wasm-esdt-safe-full/Cargo.lock index ac29f9d1..faee3be8 100644 --- a/esdt-safe/wasm-esdt-safe-full/Cargo.lock +++ b/esdt-safe/wasm-esdt-safe-full/Cargo.lock @@ -67,6 +67,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/esdt-safe/wasm-esdt-safe-view/Cargo.lock b/esdt-safe/wasm-esdt-safe-view/Cargo.lock index ed2416c2..aee268f2 100644 --- a/esdt-safe/wasm-esdt-safe-view/Cargo.lock +++ b/esdt-safe/wasm-esdt-safe-view/Cargo.lock @@ -67,6 +67,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/esdt-safe/wasm/Cargo.lock b/esdt-safe/wasm/Cargo.lock index 67435991..c26e8563 100644 --- a/esdt-safe/wasm/Cargo.lock +++ b/esdt-safe/wasm/Cargo.lock @@ -67,6 +67,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] diff --git a/header-verifier/Cargo.toml b/header-verifier/Cargo.toml index 855bfafa..d7f2a163 100644 --- a/header-verifier/Cargo.toml +++ b/header-verifier/Cargo.toml @@ -17,6 +17,9 @@ path = "../common/transaction" [dependencies.proxies] path = "../common/proxies" +[dependencies.setup-phase] +path = "../common/setup-phase" + [dev-dependencies] num-bigint = "0.4.2" diff --git a/header-verifier/src/lib.rs b/header-verifier/src/lib.rs index d01736d5..607dd4d8 100644 --- a/header-verifier/src/lib.rs +++ b/header-verifier/src/lib.rs @@ -12,7 +12,7 @@ pub enum OperationHashStatus { } #[multiversx_sc::contract] -pub trait Headerverifier { +pub trait Headerverifier: setup_phase::SetupPhaseModule { #[init] fn init(&self, bls_pub_keys: MultiValueEncoded) { for pub_key in bls_pub_keys { @@ -90,6 +90,33 @@ pub trait Headerverifier { } } + #[only_owner] + #[endpoint(completeSetupPhase)] + fn complete_setup_phase(&self) { + if self.is_setup_phase_complete() { + return; + } + + let chain_config_mapper = self.chain_config_address(); + require!( + !chain_config_mapper.is_empty(), + "The Chain-Config address is not set" + ); + + let chain_config_address = chain_config_mapper.get(); + let min_validators = self.min_validators(chain_config_address).get(); + let number_of_validators = self.bls_pub_keys().len() as u32; + + require!( + number_of_validators > min_validators, + "There should be at least {} more validators so the setup phase can be completed", + (number_of_validators - min_validators) + ); + + // change ownership + self.setup_phase_complete().set(true); + } + fn require_caller_esdt_safe(&self) { let esdt_safe_mapper = self.esdt_safe_address(); @@ -155,4 +182,10 @@ pub trait Headerverifier { #[storage_mapper("esdtSafeAddress")] fn esdt_safe_address(&self) -> SingleValueMapper; + + #[storage_mapper("chainConfigAddress")] + fn chain_config_address(&self) -> SingleValueMapper; + + #[storage_mapper_from_address("minValidators")] + fn min_validators(&self, sc_address: ManagedAddress) -> SingleValueMapper; } diff --git a/header-verifier/wasm-header-verifier-full/Cargo.lock b/header-verifier/wasm-header-verifier-full/Cargo.lock index 5b304b23..cf87ea22 100644 --- a/header-verifier/wasm-header-verifier-full/Cargo.lock +++ b/header-verifier/wasm-header-verifier-full/Cargo.lock @@ -32,6 +32,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] @@ -180,6 +181,13 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "setup-phase" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/header-verifier/wasm-header-verifier-full/src/lib.rs b/header-verifier/wasm-header-verifier-full/src/lib.rs index 67d2506f..31cdc8ff 100644 --- a/header-verifier/wasm-header-verifier-full/src/lib.rs +++ b/header-verifier/wasm-header-verifier-full/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 4 +// Endpoints: 5 // Async Callback (empty): 1 -// Total number of exported functions: 7 +// Total number of exported functions: 8 #![no_std] @@ -24,6 +24,7 @@ multiversx_sc_wasm_adapter::endpoints! { setEsdtSafeAddress => set_esdt_safe_address removeExecutedHash => remove_executed_hash lockOperationHash => lock_operation_hash + completeSetupPhase => complete_setup_phase ) } diff --git a/header-verifier/wasm-multisig-view/Cargo.lock b/header-verifier/wasm-multisig-view/Cargo.lock index 6a0be894..44446d83 100644 --- a/header-verifier/wasm-multisig-view/Cargo.lock +++ b/header-verifier/wasm-multisig-view/Cargo.lock @@ -32,6 +32,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] @@ -180,6 +181,13 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "setup-phase" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/header-verifier/wasm/Cargo.lock b/header-verifier/wasm/Cargo.lock index 33a297fb..3859caf6 100644 --- a/header-verifier/wasm/Cargo.lock +++ b/header-verifier/wasm/Cargo.lock @@ -32,6 +32,7 @@ version = "0.0.0" dependencies = [ "multiversx-sc", "proxies", + "setup-phase", "transaction", ] @@ -180,6 +181,13 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "setup-phase" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/header-verifier/wasm/src/lib.rs b/header-verifier/wasm/src/lib.rs index 67d2506f..31cdc8ff 100644 --- a/header-verifier/wasm/src/lib.rs +++ b/header-verifier/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 4 +// Endpoints: 5 // Async Callback (empty): 1 -// Total number of exported functions: 7 +// Total number of exported functions: 8 #![no_std] @@ -24,6 +24,7 @@ multiversx_sc_wasm_adapter::endpoints! { setEsdtSafeAddress => set_esdt_safe_address removeExecutedHash => remove_executed_hash lockOperationHash => lock_operation_hash + completeSetupPhase => complete_setup_phase ) } diff --git a/sovereign-forge/src/common/mod.rs b/sovereign-forge/src/common/mod.rs index 7826b9e3..60c24b24 100644 --- a/sovereign-forge/src/common/mod.rs +++ b/sovereign-forge/src/common/mod.rs @@ -1,2 +1,3 @@ +pub mod sc_deploy; pub mod storage; pub mod utils; diff --git a/sovereign-forge/src/common/sc_deploy.rs b/sovereign-forge/src/common/sc_deploy.rs new file mode 100644 index 00000000..38a73ebe --- /dev/null +++ b/sovereign-forge/src/common/sc_deploy.rs @@ -0,0 +1,48 @@ +use crate::err_msg; +use multiversx_sc::types::{MultiValueEncoded, ReturnsResult}; +use proxies::chain_factory_proxy::ChainFactoryContractProxy; +use transaction::StakeMultiArg; + +#[multiversx_sc::module] +pub trait ScDeployModule: super::utils::UtilsModule + super::storage::StorageModule { + #[inline] + fn deploy_chain_config( + &self, + min_validators: u64, + max_validators: u64, + min_stake: BigUint, + additional_stake_required: MultiValueEncoded>, + ) -> ManagedAddress { + self.tx() + .to(self.get_chain_factory_address()) + .typed(ChainFactoryContractProxy) + .deploy_sovereign_chain_config_contract( + min_validators, + max_validators, + min_stake, + additional_stake_required, + ) + .returns(ReturnsResult) + .sync_call() + } + + #[inline] + fn deploy_header_verifier(&self, bls_keys: MultiValueEncoded) -> ManagedAddress { + self.tx() + .to(self.get_chain_factory_address()) + .typed(ChainFactoryContractProxy) + .deploy_header_verifier(bls_keys) + .returns(ReturnsResult) + .sync_call() + } + + #[inline] + fn deploy_esdt_safe(&self, is_sovereign_chain: bool) -> ManagedAddress { + self.tx() + .to(self.get_chain_factory_address()) + .typed(ChainFactoryContractProxy) + .deploy_esdt_safe(is_sovereign_chain) + .returns(ReturnsResult) + .sync_call() + } +} diff --git a/sovereign-forge/src/common/storage.rs b/sovereign-forge/src/common/storage.rs index 8d923531..5df3bdbf 100644 --- a/sovereign-forge/src/common/storage.rs +++ b/sovereign-forge/src/common/storage.rs @@ -1,19 +1,24 @@ -use multiversx_sc::imports::{SingleValueMapper, UnorderedSetMapper}; +use multiversx_sc::{ + imports::{SingleValueMapper, UnorderedSetMapper}, + types::ManagedBuffer, +}; use super::utils::ContractInfo; +pub type ChainId = ManagedBuffer; + #[multiversx_sc::module] pub trait StorageModule { #[storage_mapper("sovereignsMapper")] fn sovereigns_mapper( &self, sovereign_creator: &ManagedAddress, - ) -> SingleValueMapper; + ) -> SingleValueMapper>; #[storage_mapper("sovereignDeployedContracts")] fn sovereign_deployed_contracts( &self, - chain_id: &ManagedBuffer, + chain_id: &ChainId, ) -> UnorderedSetMapper>; #[view(getChainFactoryAddress)] diff --git a/sovereign-forge/src/common/utils.rs b/sovereign-forge/src/common/utils.rs index 916b7786..a88c8b57 100644 --- a/sovereign-forge/src/common/utils.rs +++ b/sovereign-forge/src/common/utils.rs @@ -29,8 +29,8 @@ impl ContractInfo { pub enum ScArray { ChainFactory, Controller, - SovereignHeaderVerifier, - SovereignCrossChainOperation, + HeaderVerifier, + ESDTSafe, FeeMarket, TokenHandler, ChainConfig, @@ -39,6 +39,37 @@ pub enum ScArray { #[multiversx_sc::module] pub trait UtilsModule: super::storage::StorageModule { + fn require_phase_1_completed(&self, caller: &ManagedAddress) { + require!( + self.sovereigns_mapper(caller).is_empty(), + "The current caller has not deployed any Sovereign Chain" + ); + + require!( + self.is_contract_deployed(caller, ScArray::ChainConfig), + "The Chain-Config SC is not deployed" + ); + + require!( + !self.is_contract_deployed(caller, ScArray::HeaderVerifier), + "The Header-Verifier SC is already deployed" + ); + } + + fn is_contract_deployed(&self, sovereign_creator: &ManagedAddress, sc_id: ScArray) -> bool { + let sovereigns_mapper = self.sovereigns_mapper(sovereign_creator); + + require!( + !sovereigns_mapper.is_empty(), + "There are no contracts deployed for this Sovereign" + ); + + let chain_id = sovereigns_mapper.get(); + let deployed_contracts_mapper = self.sovereign_deployed_contracts(&chain_id); + + deployed_contracts_mapper.iter().any(|sc| sc.id == sc_id) + } + fn generate_chain_id(&self) -> ManagedBuffer { loop { let new_chain_id = self.generated_random_4_char_string(); @@ -66,4 +97,11 @@ pub trait UtilsModule: super::storage::StorageModule { "The given deploy cost is not equal to the standard amount" ); } + + fn get_chain_factory_address(&self) -> ManagedAddress { + let caller = self.blockchain().get_caller(); + let shard_id = self.blockchain().get_shard_of_address(&caller); + + self.chain_factories(shard_id).get() + } } diff --git a/sovereign-forge/src/lib.rs b/sovereign-forge/src/lib.rs index bf5551be..9d7023dc 100644 --- a/sovereign-forge/src/lib.rs +++ b/sovereign-forge/src/lib.rs @@ -11,6 +11,7 @@ pub trait SovereignForge: phases::PhasesModule + common::storage::StorageModule + common::utils::UtilsModule + + common::sc_deploy::ScDeployModule + setup_phase::SetupPhaseModule { #[init] diff --git a/sovereign-forge/src/phases.rs b/sovereign-forge/src/phases.rs index 30a7f71b..59e0fc98 100644 --- a/sovereign-forge/src/phases.rs +++ b/sovereign-forge/src/phases.rs @@ -1,12 +1,8 @@ use crate::err_msg; use core::ops::Deref; -use proxies::chain_factory_proxy::ChainFactoryContractProxy; use transaction::StakeMultiArg; -use multiversx_sc::{ - require, - types::{MultiValueEncoded, ReturnsResult}, -}; +use multiversx_sc::{require, types::MultiValueEncoded}; use crate::common::{ self, @@ -17,7 +13,10 @@ const NUMBER_OF_SHARDS: u32 = 3; #[multiversx_sc::module] pub trait PhasesModule: - common::utils::UtilsModule + common::storage::StorageModule + setup_phase::SetupPhaseModule + common::utils::UtilsModule + + common::storage::StorageModule + + setup_phase::SetupPhaseModule + + common::sc_deploy::ScDeployModule { #[only_owner] #[endpoint(completeSetupPhase)] @@ -68,81 +67,39 @@ pub trait PhasesModule: caller_shard_id ); - let chain_factory_address = chain_factories_mapper.get(); + require!( + !self.is_contract_deployed(&caller, ScArray::ChainConfig), + "The Chain-Factory Contract is already deployed" + ); let chain_config_address = self.deploy_chain_config( - chain_factory_address, min_validators, max_validators, min_stake, additional_stake_required, ); - let sovereigns_mapper = self.sovereigns_mapper(&caller); - require!( - sovereigns_mapper.is_empty(), - "There is already a deployed Sovereign Chain for this user" - ); - let chain_factory_contract_info = ContractInfo::new(ScArray::ChainConfig, chain_config_address); self.sovereign_deployed_contracts(&chain_id) .insert(chain_factory_contract_info); - sovereigns_mapper.set(chain_id); + self.sovereigns_mapper(&caller).set(chain_id); } #[endpoint(deployPhaseTwo)] - fn deploy_phase_two(&self) { - // check chain config was deployed && header was not - // deploy header - // update mapper - } + fn deploy_phase_two(&self, bls_keys: MultiValueEncoded) { + let blockchain_api = self.blockchain(); + let caller = blockchain_api.get_caller(); - fn deploy_chain_config( - &self, - chain_factory_address: ManagedAddress, - min_validators: u64, - max_validators: u64, - min_stake: BigUint, - additional_stake_required: MultiValueEncoded>, - ) -> ManagedAddress { - self.tx() - .to(chain_factory_address) - .typed(ChainFactoryContractProxy) - .deploy_sovereign_chain_config_contract( - min_validators, - max_validators, - min_stake, - additional_stake_required, - ) - .returns(ReturnsResult) - .sync_call() - } + self.require_phase_1_completed(&caller); - fn deploy_header_verifier( - &self, - chain_factory_address: ManagedAddress, - bls_keys: MultiValueEncoded, - ) -> ManagedAddress { - self.tx() - .to(chain_factory_address) - .typed(ChainFactoryContractProxy) - .deploy_header_verifier(bls_keys) - .returns(ReturnsResult) - .sync_call() - } + let header_verifier_address = self.deploy_header_verifier(bls_keys); - fn deploy_esdt_safe( - &self, - chain_factory_address: ManagedAddress, - is_sovereign_chain: bool, - ) -> ManagedAddress { - self.tx() - .to(chain_factory_address) - .typed(ChainFactoryContractProxy) - .deploy_esdt_safe(is_sovereign_chain) - .returns(ReturnsResult) - .sync_call() + let header_verifier_contract_info = + ContractInfo::new(ScArray::HeaderVerifier, header_verifier_address); + + self.sovereign_deployed_contracts(&self.sovereigns_mapper(&caller).get()) + .insert(header_verifier_contract_info); } } diff --git a/sovereign-forge/tests/sovereign_forge_unit_tests.rs b/sovereign-forge/tests/sovereign_forge_unit_tests.rs index bc74f31b..42124551 100644 --- a/sovereign-forge/tests/sovereign_forge_unit_tests.rs +++ b/sovereign-forge/tests/sovereign_forge_unit_tests.rs @@ -1,4 +1,4 @@ -use multiversx_sc::types::{BigUint, MultiValueEncoded, TestAddress, TestSCAddress}; +use multiversx_sc::types::{BigUint, ManagedBuffer, MultiValueEncoded, TestAddress, TestSCAddress}; use multiversx_sc_scenario::{ api::StaticApi, imports::MxscPath, ExpectError, ScenarioTxRun, ScenarioTxWhitebox, ScenarioWorld, @@ -167,6 +167,26 @@ impl SovereignForgeTestState { } } + fn deploy_phase_two( + &mut self, + expected_result: Option, + bls_keys: MultiValueEncoded>, + ) { + let transaction = self + .world + .tx() + .from(OWNER_ADDRESS) + .to(FORGE_ADDRESS) + .typed(SovereignForgeProxy) + .deploy_phase_two(bls_keys); + + if let Some(error) = expected_result { + transaction.returns(error).run(); + } else { + transaction.run(); + } + } + fn finish_setup(&mut self) { self.register_chain_factory(1, FACTORY_ADDRESS, None); self.register_chain_factory(2, FACTORY_ADDRESS, None); @@ -292,6 +312,27 @@ fn deploy_phase_one_chain_config_missing() { 2, BigUint::from(2u32), MultiValueEncoded::new(), - Some(ExpectError(10, "error signalled by smartcontract")), + Some(ExpectError( + 4, + "There are no contracts deployed for this Sovereign", + )), + ); +} + +#[test] +fn deploy_phase_two_without_first_phase() { + let mut state = SovereignForgeTestState::new(); + state.deploy_sovereign_forge(); + state.deploy_chain_factory(); + state.finish_setup(); + + let bls_keys = MultiValueEncoded::>::new(); + + state.deploy_phase_two( + Some(ExpectError( + 4, + "There are no contracts deployed for this Sovereign", + )), + bls_keys, ); }