Skip to content

Commit

Permalink
Merge pull request #219 from multiversx/feat/chain-config
Browse files Browse the repository at this point in the history
`feat/chain-config` into `feat/beta`
  • Loading branch information
andreiblt1304 authored Jan 9, 2025
2 parents d95baf7 + cdfc14b commit fe87180
Show file tree
Hide file tree
Showing 33 changed files with 378 additions and 419 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions chain-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ version = "=0.55.0"
[dependencies.operation]
path = "../common/operation"

[dependencies.proxies]
path = "../common/proxies"

[dependencies.setup-phase]
path = "../common/setup-phase"
72 changes: 16 additions & 56 deletions chain-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#![no_std]

use multiversx_sc_modules::only_admin;
use operation::aliases::StakeMultiArg;
use validator_rules::TokenIdAmountPair;
use operation::SovereignConfig;

multiversx_sc::imports!();

Expand All @@ -13,72 +12,33 @@ pub trait ChainConfigContract:
validator_rules::ValidatorRulesModule + only_admin::OnlyAdminModule + setup_phase::SetupPhaseModule
{
#[init]
fn init(
&self,
min_validators: u64,
max_validators: u64,
min_stake: BigUint,
admin: ManagedAddress,
additional_stake_required: MultiValueEncoded<StakeMultiArg<Self::Api>>,
) {
require!(
min_validators <= max_validators,
"Invalid min/max validator numbers"
);

let mut additional_stake_vec = ManagedVec::new();
for multi_value in additional_stake_required {
let (token_id, amount) = multi_value.into_tuple();
let value = TokenIdAmountPair { token_id, amount };

additional_stake_vec.push(value);
}

self.min_validators().set(min_validators);
self.max_validators().set(max_validators);
self.min_stake().set(min_stake);
fn init(&self, config: SovereignConfig<Self::Api>, admin: ManagedAddress) {
self.require_valid_config(&config);
self.sovereign_config().set(config.clone());
self.add_admin(admin);
self.additional_stake_required().set(additional_stake_vec);
}

#[only_admin]
fn update_config(
&self,
opt_min_validators: Option<u64>,
opt_max_validators: Option<u64>,
opt_min_stake: Option<BigUint>,
opt_additional_stake_required: Option<MultiValueEncoded<StakeMultiArg<Self::Api>>>,
) {
if let Some(min_validators) = opt_min_validators {
self.min_validators().set(min_validators);
}
if let Some(max_validators) = opt_max_validators {
self.max_validators().set(max_validators);
}
if let Some(min_stake) = opt_min_stake {
self.min_stake().set(min_stake);
}
if let Some(additional_stake_required) = opt_additional_stake_required {
let mut additional_stake_vec = ManagedVec::new();
for multi_value in additional_stake_required {
let (token_id, amount) = multi_value.into_tuple();
let value = TokenIdAmountPair { token_id, amount };

additional_stake_vec.push(value);
}
self.additional_stake_required().set(additional_stake_vec);
}
#[endpoint(updateConfig)]
fn update_config(&self, new_config: SovereignConfig<Self::Api>) {
self.require_valid_config(&new_config);
self.sovereign_config().set(new_config);
}

#[only_owner]
fn complete_setup_phase(&self) {
#[endpoint(completeSetupPhase)]
fn complete_setup_phase(&self, header_verifier_address: ManagedAddress) {
if self.is_setup_phase_complete() {
return;
}

self.require_config_set();
// validator set in header verifier
// change ownership to header-verifier
self.tx()
.to(ToSelf)
.typed(UserBuiltinProxy)
.change_owner_address(&header_verifier_address)
.sync_call();

self.setup_phase_complete().set(true);
}

Expand Down
48 changes: 13 additions & 35 deletions chain-config/src/validator_rules.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use operation::SovereignConfig;

multiversx_sc::imports!();
multiversx_sc::derive_imports!();

Expand All @@ -13,45 +15,21 @@ pub struct TokenIdAmountPair<M: ManagedTypeApi> {

#[multiversx_sc::module]
pub trait ValidatorRulesModule {
fn require_config_set(&self) {
require!(
!self.min_validators().is_empty(),
"The minimum number of validators is not set"
);
require!(
!self.max_validators().is_empty(),
"The maximum number of validators is not set"
);
require!(
!self.min_stake().is_empty(),
"The mininum number of stake is not set"
);
fn require_valid_config(&self, config: &SovereignConfig<Self::Api>) {
// TODO: determine a range value
self.require_validator_range(config.min_validators, config.max_validators);
}

fn require_validator_range(&self, min_validators: u64, max_validators: u64) {
require!(
!self.additional_stake_required().is_empty(),
"The additional stake criteria is not set"
min_validators <= max_validators,
"Invalid min/max validator numbers"
);
}

#[view(getMinValidators)]
#[storage_mapper("minValidators")]
fn min_validators(&self) -> SingleValueMapper<u64>;

#[view(getMaxValidators)]
#[storage_mapper("maxValidators")]
fn max_validators(&self) -> SingleValueMapper<u64>;

// TODO: Read user stake and verify
#[view(getMinStake)]
#[storage_mapper("minStake")]
fn min_stake(&self) -> SingleValueMapper<BigUint>;

// NOTE: ManagedVec or MultiValueEncoded ?
// TODO: Read user stake and verify
#[view(getAdditionalStakeRequired)]
#[storage_mapper("additionalStakeRequired")]
fn additional_stake_required(
&self,
) -> SingleValueMapper<ManagedVec<TokenIdAmountPair<Self::Api>>>;
#[view(sovereignConfig)]
#[storage_mapper("sovereignConfig")]
fn sovereign_config(&self) -> SingleValueMapper<SovereignConfig<Self::Api>>;

#[view(wasPreviouslySlashed)]
#[storage_mapper("wasPreviouslySlashed")]
Expand Down
128 changes: 128 additions & 0 deletions chain-config/tests/chain_config_unit_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use multiversx_sc::types::{BigUint, TestAddress, TestSCAddress};
use multiversx_sc_scenario::{
api::StaticApi, imports::MxscPath, ExpectError, ScenarioTxRun, ScenarioWorld,
};
use proxies::chain_config_proxy::ChainConfigContractProxy;
use operation::SovereignConfig;

const CONFIG_ADDRESS: TestSCAddress = TestSCAddress::new("config-address");
const CONFIG_CODE_PATH: MxscPath = MxscPath::new("output/chain-config.mxsc.json");

const HEADER_VERIFIER_ADDRESS: TestSCAddress = TestSCAddress::new("header-verifier");

const OWNER: TestAddress = TestAddress::new("owner");
const OWNER_BALANCE: u64 = 100_000_000_000;

fn world() -> ScenarioWorld {
let mut blockchain = ScenarioWorld::new();

blockchain.register_contract(CONFIG_CODE_PATH, chain_config::ContractBuilder);

blockchain
}

struct ChainConfigTestState {
world: ScenarioWorld,
}

impl ChainConfigTestState {
fn new() -> Self {
let mut world = world();

world.account(OWNER).balance(OWNER_BALANCE).nonce(1);

Self { world }
}

fn deploy_chain_config(&mut self, config: SovereignConfig<StaticApi>, admin: TestAddress) {
self.world
.tx()
.from(OWNER)
.typed(ChainConfigContractProxy)
.init(config, admin)
.code(CONFIG_CODE_PATH)
.new_address(CONFIG_ADDRESS)
.run();
}

fn update_chain_config(
&mut self,
config: SovereignConfig<StaticApi>,
expect_error: Option<ExpectError>,
) {
let transaction = self
.world
.tx()
.from(OWNER)
.to(CONFIG_ADDRESS)
.typed(ChainConfigContractProxy)
.update_config(config);

if let Some(error) = expect_error {
transaction.returns(error).run();
} else {
transaction.run();
}
}

fn complete_setup_phase(&mut self, expect_error: Option<ExpectError>) {
let transaction = self
.world
.tx()
.from(OWNER)
.to(CONFIG_ADDRESS)
.typed(ChainConfigContractProxy)
.complete_setup_phase(HEADER_VERIFIER_ADDRESS);

if let Some(error) = expect_error {
transaction.returns(error).run();
} else {
transaction.run();
}
}
}

#[test]
fn deploy_chain_config() {
let mut state = ChainConfigTestState::new();

let config = SovereignConfig::new(0, 1, BigUint::default(), None);
state.deploy_chain_config(config, OWNER);
}

#[test]
fn update_config() {
let mut state = ChainConfigTestState::new();

let config = SovereignConfig::new(0, 1, BigUint::default(), None);
state.deploy_chain_config(config, OWNER);

let new_config = SovereignConfig::new(2, 4, BigUint::default(), None);

state.update_chain_config(new_config, None);
}

#[test]
fn update_config_wrong_validators_array() {
let mut state = ChainConfigTestState::new();

let config = SovereignConfig::new(0, 1, BigUint::default(), None);
state.deploy_chain_config(config, OWNER);

let new_config = SovereignConfig::new(2, 1, BigUint::default(), None);

state.update_chain_config(
new_config,
Some(ExpectError(4, "Invalid min/max validator numbers")),
);
}

#[test]
fn complete_setup_phase() {
let mut state = ChainConfigTestState::new();

let config = SovereignConfig::new(0, 1, BigUint::default(), None);
state.deploy_chain_config(config, OWNER);

state.complete_setup_phase(None);
}
18 changes: 18 additions & 0 deletions chain-config/wasm-chain-config-full/Cargo.lock

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

11 changes: 5 additions & 6 deletions chain-config/wasm-chain-config-full/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

// Init: 1
// Upgrade: 1
// Endpoints: 9
// Endpoints: 8
// Async Callback (empty): 1
// Total number of exported functions: 12
// Total number of exported functions: 11

#![no_std]

Expand All @@ -20,10 +20,9 @@ multiversx_sc_wasm_adapter::endpoints! {
(
init => init
upgrade => upgrade
getMinValidators => min_validators
getMaxValidators => max_validators
getMinStake => min_stake
getAdditionalStakeRequired => additional_stake_required
updateConfig => update_config
completeSetupPhase => complete_setup_phase
sovereignConfig => sovereign_config
wasPreviouslySlashed => was_previously_slashed
isAdmin => is_admin
addAdmin => add_admin
Expand Down
Loading

0 comments on commit fe87180

Please sign in to comment.