Skip to content

Commit

Permalink
implementation of migration
Browse files Browse the repository at this point in the history
  • Loading branch information
dusan-maksimovic committed Jan 27, 2025
1 parent 41dc0f1 commit 85fc37e
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 17 deletions.
9 changes: 6 additions & 3 deletions contracts/hydro/src/lsm_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ pub fn initialize_validator_store_helper(
}

// copy the information from the previous round
let val_infos = load_validators_infos(storage, round_id - 1)?;
let val_infos = load_validators_infos(storage, round_id - 1);

for val_info in val_infos {
let address = val_info.clone().address;
Expand All @@ -426,10 +426,13 @@ pub fn initialize_validator_store_helper(
}

// load_validators_infos needs to be its own function to borrow the storage
fn load_validators_infos(storage: &dyn Storage, round_id: u64) -> StdResult<Vec<ValidatorInfo>> {
pub fn load_validators_infos(storage: &dyn Storage, round_id: u64) -> Vec<ValidatorInfo> {
VALIDATORS_INFO
.prefix(round_id)
.range(storage, None, None, Order::Ascending)
.map(|val_info_res| val_info_res.map(|val_info| val_info.1))
.filter_map(|val_info| match val_info {
Err(_) => None,
Ok(val_info) => Some(val_info.1),
})
.collect()
}
2 changes: 1 addition & 1 deletion contracts/hydro/src/migration/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::v3_0_0::MigrateMsgV3_0_0;
pub const CONTRACT_VERSION_V1_1_0: &str = "1.1.0";
pub const CONTRACT_VERSION_V2_0_1: &str = "2.0.1";
pub const CONTRACT_VERSION_V2_0_2: &str = "2.0.2";
pub const CONTRACT_VERSION_V2_1_0: &str = "2.0.2";
pub const CONTRACT_VERSION_V2_1_0: &str = "2.1.0";
pub const CONTRACT_VERSION_V3_0_0: &str = "3.0.0";
pub const CONTRACT_VERSION_UNRELEASED: &str = "4.0.0";

Expand Down
191 changes: 178 additions & 13 deletions contracts/hydro/src/migration/unreleased.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,185 @@
use cosmwasm_std::{DepsMut, Env};
use std::collections::HashMap;

use cosmwasm_std::{Addr, Decimal, DepsMut, Env, Order, StdResult};
use cw_storage_plus::{Item, Map};
use neutron_sdk::bindings::query::NeutronQuery;

use crate::error::ContractError;
use crate::{
contract::compute_current_round_id,
error::ContractError,
lsm_integration::load_validators_infos,
migration::v3_0_0::ConstantsV3_0_0,
state::{
Constants, HeightRange, LockEntry, CONSTANTS, HEIGHT_TO_ROUND, LOCKS_MAP,
ROUND_TO_HEIGHT_RANGE, SCALED_ROUND_POWER_SHARES_MAP, TOTAL_VOTING_POWER_PER_ROUND,
USER_LOCKS,
},
};

pub fn migrate_v3_0_0_to_unreleased(
_deps: &mut DepsMut<NeutronQuery>,
_env: Env,
deps: &mut DepsMut<NeutronQuery>,
env: Env,
) -> Result<(), ContractError> {
// TODO:
// 1) Migrate Constants from Item to Map; Make sure that the queries for past rounds keep working.
// 2) TOTAL_VOTING_POWER_PER_ROUND needs to be correctly populated regardless of the point in time
// we do the migration. Needs to be populated for future rounds as well. If we populate it for
// the past rounds as well, we can use that in our queries instead of on-the-fly computation
// e.g. query_round_total_power(), query_top_n_proposals().
// 3) LOCKS_MAP needs to be migrated to SnapshotMap.
// 4) Populate USER_LOCKS for existing lockups.
// 4) Populate ROUND_TO_HEIGHT_RANGE and HEIGHT_TO_ROUND for previous rounds?
let constants = migrate_constants(deps)?;
let round_id = compute_current_round_id(&env, &constants)?;

populate_total_power_for_rounds(deps, &env, round_id)?;
migrate_user_lockups(deps, &env)?;
populate_round_height_mappings(deps, &env, round_id)?;

Ok(())
}

// Convert CONSTANTS storage from Item to Map and insert single constants instance
// under the timestamp of the first round start time, and set the extra_cap to zero.
fn migrate_constants(deps: &mut DepsMut<NeutronQuery>) -> StdResult<Constants> {
const OLD_CONSTANTS: Item<ConstantsV3_0_0> = Item::new("constants");
let old_constants = OLD_CONSTANTS.load(deps.storage)?;

let new_constants = Constants {
round_length: old_constants.round_length,
lock_epoch_length: old_constants.lock_epoch_length,
first_round_start: old_constants.first_round_start,
max_locked_tokens: old_constants.max_locked_tokens,
max_validator_shares_participating: old_constants.max_validator_shares_participating,
hub_connection_id: old_constants.hub_connection_id,
hub_transfer_channel_id: old_constants.hub_transfer_channel_id,
icq_update_period: old_constants.icq_update_period,
paused: old_constants.paused,
max_deployment_duration: old_constants.max_deployment_duration,
round_lock_power_schedule: old_constants.round_lock_power_schedule,
current_users_extra_cap: 0, // set the extra cap to 0 during the migration
};

OLD_CONSTANTS.remove(deps.storage);
CONSTANTS.save(
deps.storage,
new_constants.first_round_start.nanos(),
&new_constants,
)?;

Ok(new_constants)
}

// Populate round total power starting from round 0 and all the way to the last round
// in which any existing lock gives voting power.
fn populate_total_power_for_rounds(
deps: &mut DepsMut<NeutronQuery>,
env: &Env,
current_round_id: u64,
) -> StdResult<()> {
let current_validator_ratios = load_validators_infos(deps.storage, current_round_id)
.iter()
.map(|validator_info| (validator_info.address.clone(), validator_info.power_ratio))
.collect();

let mut round_id = 0;
loop {
let validator_power_ratios: &HashMap<String, Decimal> = if round_id >= current_round_id {
&current_validator_ratios
} else {
&load_validators_infos(deps.storage, round_id)
.iter()
.map(|validator_info| (validator_info.address.clone(), validator_info.power_ratio))
.collect()
};

let round_validator_shares = SCALED_ROUND_POWER_SHARES_MAP
.prefix(round_id)
.range(deps.storage, None, None, Order::Ascending)
.filter_map(|val_shares| match val_shares {
Err(_) => None,
Ok(val_shares) => Some(val_shares),
})
.collect::<Vec<(String, Decimal)>>();

// When we encounter the round with zero shares of any validator, it means that there
// was no lock entry that would give voting power for the given round, or any subsequent
// rounds, so we break the loop at that point.
if round_validator_shares.is_empty() {
break;
}

let round_total_power: Decimal = round_validator_shares
.iter()
.map(|validator_shares| {
validator_power_ratios
.get(&validator_shares.0)
.map_or_else(Decimal::zero, |power_ratio| {
power_ratio * validator_shares.1
})
})
.sum();

TOTAL_VOTING_POWER_PER_ROUND.save(
deps.storage,
round_id,
&round_total_power.to_uint_ceil(),
env.block.height,
)?;

round_id += 1;
}

Ok(())
}

// Converts the LOCKS_MAP from Map into SnapshotMap and populates USER_LOCKS map.
fn migrate_user_lockups(deps: &mut DepsMut<NeutronQuery>, env: &Env) -> StdResult<()> {
const OLD_LOCKS_MAP: Map<(Addr, u64), LockEntry> = Map::new("locks_map");

let mut user_locks_map: HashMap<Addr, Vec<u64>> = HashMap::new();
let user_lockups: Vec<(Addr, LockEntry)> = OLD_LOCKS_MAP
.range(deps.storage, None, None, Order::Ascending)
.filter_map(|lockup| match lockup {
Err(_) => None,
Ok(lockup) => {
user_locks_map
.entry(lockup.0 .0.clone())
.and_modify(|user_locks| user_locks.push(lockup.1.lock_id))
.or_insert(vec![lockup.1.lock_id]);

Some((lockup.0 .0, lockup.1))
}
})
.collect();

for user_lockup in &user_lockups {
OLD_LOCKS_MAP.remove(deps.storage, (user_lockup.0.clone(), user_lockup.1.lock_id));
}

for user_lockup in user_lockups {
LOCKS_MAP.save(
deps.storage,
(user_lockup.0.clone(), user_lockup.1.lock_id),
&user_lockup.1,
env.block.height,
)?;
}

for user_locks in user_locks_map {
USER_LOCKS.save(deps.storage, user_locks.0, &user_locks.1, env.block.height)?;
}

Ok(())
}

// Populates ROUND_TO_HEIGHT_RANGE and HEIGHT_TO_ROUND maps
fn populate_round_height_mappings(
deps: &mut DepsMut<NeutronQuery>,
env: &Env,
current_round_id: u64,
) -> StdResult<()> {
ROUND_TO_HEIGHT_RANGE.save(
deps.storage,
current_round_id,
&HeightRange {
lowest_known_height: env.block.height,
highest_known_height: env.block.height,
},
)?;

HEIGHT_TO_ROUND.save(deps.storage, env.block.height, &current_round_id)?;

Ok(())
}
19 changes: 19 additions & 0 deletions contracts/hydro/src/migration/v3_0_0.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Timestamp;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::state::RoundLockPowerSchedule;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct MigrateMsgV3_0_0 {}

#[cw_serde]
pub struct ConstantsV3_0_0 {
pub round_length: u64,
pub lock_epoch_length: u64,
pub first_round_start: Timestamp,
pub max_locked_tokens: u128,
pub max_validator_shares_participating: u64,
pub hub_connection_id: String,
pub hub_transfer_channel_id: String,
pub icq_update_period: u64,
pub paused: bool,
pub max_deployment_duration: u64,
pub round_lock_power_schedule: RoundLockPowerSchedule,
}

0 comments on commit 85fc37e

Please sign in to comment.