Initial mixnet v1->v2 migration code

This commit is contained in:
Jędrzej Stuczyński
2022-08-25 15:22:52 +01:00
parent a6aba3defd
commit 58b23e6ee4
6 changed files with 586 additions and 258 deletions
Generated
+2 -2
View File
@@ -4993,9 +4993,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa 1.0.1",
"ryu",
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::reward_params::NodeRewardParams;
use crate::ContractStateParams;
use crate::{ContractStateParams, Layer, SphinxKey};
use crate::{Gateway, IdentityKey, MixNode};
use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -213,17 +214,121 @@ pub enum QueryMsg {
},
}
// all of those `serde rename` are here to reduce the rpc response size to bare minimum
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(rename = "op")]
pub enum V2MigrationOperation {
#[serde(rename = "m1")]
MigrateOperator {
#[serde(rename = "a1")]
node_identity: String,
},
#[serde(rename = "m2")]
MigrateDelegator {
#[serde(rename = "a1")]
address: Addr,
#[serde(rename = "a2")]
node_identity: String,
#[serde(rename = "a3")]
proxy: Option<Addr>,
#[serde(rename = "a4")]
new_mix_id: Option<u64>,
},
#[serde(rename = "m3")]
RemoveOperator {
#[serde(rename = "a1")]
node_identity: String,
},
#[serde(rename = "m4")]
RemoveDelegator {
#[serde(rename = "a1")]
address: Addr,
#[serde(rename = "a2")]
node_identity: String,
#[serde(rename = "a3")]
proxy: Option<Addr>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum SpecialVestingMigrateExecuteMsg {
UpdateDelegation {
owner: String,
node_identity: String,
mix_id: u64,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum SpecialV2ExecuteMsg {
ToBeNamedSaveMixnode {
#[serde(rename = "a1")]
host: String,
#[serde(rename = "a2")]
mix_port: u16,
#[serde(rename = "a3")]
verloc_port: u16,
#[serde(rename = "a4")]
http_api_port: u16,
#[serde(rename = "a5")]
sphinx_key: SphinxKey,
#[serde(rename = "a6")]
identity_key: IdentityKey,
#[serde(rename = "a7")]
version: String,
#[serde(rename = "a8")]
pledge_amount: Coin,
#[serde(rename = "a9")]
owner: Addr,
#[serde(rename = "a10")]
block_height: u64,
#[serde(rename = "a11")]
proxy: Option<Addr>,
},
ToBeNamedSaveDelegation {
#[serde(rename = "a1")]
owner: Addr,
#[serde(rename = "a2")]
mix_id: u64,
#[serde(rename = "a3")]
amount: Coin,
#[serde(rename = "a4")]
block_height: u64,
#[serde(rename = "a5")]
proxy: Option<Addr>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {
pub mixnet_denom: String,
nodes_to_remove: Option<Vec<NodeToRemove>>,
}
impl MigrateMsg {
pub fn nodes_to_remove(&self) -> Vec<NodeToRemove> {
self.nodes_to_remove.clone().unwrap_or_default()
}
pub v2_contract_address: String,
pub vesting_contract_address: String,
pub operations: Vec<V2MigrationOperation>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
@@ -119,6 +119,12 @@ pub enum ExecuteMsg {
UpdateLockedPledgeCap {
amount: Uint128,
},
AuthorisedUpdateToV2 {
owner: String,
node_identity: IdentityKey,
mix_id: u64,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+215 -220
View File
@@ -7,6 +7,7 @@ use crate::delegations::queries::query_mixnode_delegation;
use crate::delegations::queries::{
query_mixnode_delegations_paged, query_pending_delegation_events,
};
use crate::delegations::storage::delegations;
use crate::error::ContractError;
use crate::gateways::queries::query_owns_gateway;
use crate::gateways::queries::{query_gateway_bond, query_gateways_paged};
@@ -24,18 +25,19 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address;
use crate::mixnodes::bonding_queries as mixnode_queries;
use crate::mixnodes::bonding_queries::{
query_checkpoints_for_mixnode, query_mixnode_at_height, query_mixnodes_paged,
query_checkpoints_for_mixnode, query_mixnode_at_height, query_mixnode_bond,
query_mixnodes_paged,
};
use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::mixnodes::transactions::_try_remove_mixnode;
use crate::queued_migrations::migrate_config_from_env;
use crate::queued_migrations::v2_migration;
use crate::rewards::queries::{
query_circulating_supply, query_reward_pool, query_rewarding_status, query_staking_supply,
};
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Api, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
Storage, Uint128,
Storage, SubMsg, Uint128,
};
use mixnet_contract_common::{
ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, NodeToRemove, QueryMsg,
@@ -114,6 +116,7 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
// we must be able to perform universal compound alongside reconcile
ExecuteMsg::CompoundReward {
operator,
delegator,
@@ -127,215 +130,217 @@ pub fn execute(
mix_identity,
proxy,
),
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
try_update_rewarding_validator_address(deps, info, address)
}
ExecuteMsg::InitEpoch {} => try_init_epoch(info, deps.storage, env),
ExecuteMsg::BondMixnode {
mix_node,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode(
deps,
env,
info,
mix_node,
owner_signature,
),
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(&env, deps.storage, deps.api, info)
}
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => crate::mixnodes::transactions::try_update_mixnode_config(
deps,
env,
info,
profit_margin_percent,
),
ExecuteMsg::UpdateMixnodeConfigOnBehalf {
profit_margin_percent,
owner,
} => crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
deps,
env,
info,
profit_margin_percent,
owner,
),
ExecuteMsg::BondGateway {
gateway,
owner_signature,
} => crate::gateways::transactions::try_add_gateway(
deps,
env,
info,
gateway,
owner_signature,
),
ExecuteMsg::UnbondGateway {} => {
crate::gateways::transactions::try_remove_gateway(deps, info)
}
ExecuteMsg::UpdateContractStateParams(params) => {
crate::mixnet_contract_settings::transactions::try_update_contract_settings(
deps, info, params,
)
}
ExecuteMsg::RewardMixnode { identity, params } => {
crate::rewards::transactions::try_reward_mixnode(deps, env, info, identity, params)
}
ExecuteMsg::DelegateToMixnode { mix_identity } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
}
ExecuteMsg::UndelegateFromMixnode { mix_identity } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode(
deps,
env,
info,
mix_identity,
)
}
// ExecuteMsg::RewardNextMixDelegators {
// mix_identity,
// interval_id,
// } => crate::rewards::transactions::try_reward_next_mixnode_delegators(
// deps,
// info,
// mix_identity,
// interval_id,
// ),
ExecuteMsg::DelegateToMixnodeOnBehalf {
mix_identity,
delegate,
} => crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
deps,
env,
info,
mix_identity,
delegate,
),
ExecuteMsg::UndelegateFromMixnodeOnBehalf {
mix_identity,
delegate,
} => crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
deps,
env,
info,
mix_identity,
delegate,
),
ExecuteMsg::BondMixnodeOnBehalf {
mix_node,
owner,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode_on_behalf(
deps,
env,
info,
mix_node,
owner,
owner_signature,
),
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(
&env,
deps.storage,
deps.api,
info,
owner,
)
}
ExecuteMsg::BondGatewayOnBehalf {
gateway,
owner,
owner_signature,
} => crate::gateways::transactions::try_add_gateway_on_behalf(
deps,
env,
info,
gateway,
owner,
owner_signature,
),
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
ExecuteMsg::WriteRewardedSet {
rewarded_set,
expected_active_set_size,
} => crate::interval::transactions::try_write_rewarded_set(
deps,
env,
info,
rewarded_set,
expected_active_set_size,
),
ExecuteMsg::AdvanceCurrentEpoch {} => crate::interval::transactions::try_advance_epoch(
env,
deps.storage,
info.sender.to_string(),
),
ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
crate::rewards::transactions::try_compound_delegator_reward(
deps,
env,
info,
mix_identity,
)
}
ExecuteMsg::CompoundOperatorReward {} => {
crate::rewards::transactions::try_compound_operator_reward(deps, env, info)
}
ExecuteMsg::CompoundDelegatorRewardOnBehalf {
owner,
mix_identity,
} => crate::rewards::transactions::try_compound_delegator_reward_on_behalf(
deps,
env,
info,
owner,
mix_identity,
),
ExecuteMsg::CompoundOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_compound_operator_reward_on_behalf(
deps, env, info, owner,
)
}
ExecuteMsg::ReconcileDelegations {} => {
crate::delegations::transactions::try_reconcile_all_delegation_events(deps)
}
ExecuteMsg::CheckpointMixnodes {} => {
crate::mixnodes::transactions::try_checkpoint_mixnodes(
deps.storage,
env.block.height,
info,
)
}
ExecuteMsg::ClaimOperatorReward {} => {
crate::rewards::transactions::try_claim_operator_reward(deps, &env, &info)
}
ExecuteMsg::ClaimOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_claim_operator_reward_on_behalf(
deps, &env, &info, owner,
)
}
ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
crate::rewards::transactions::try_claim_delegator_reward(
deps,
&env,
&info,
&mix_identity,
)
}
ExecuteMsg::ClaimDelegatorRewardOnBehalf {
mix_identity,
owner,
} => crate::rewards::transactions::try_claim_delegator_reward_on_behalf(
deps,
&env,
&info,
owner,
&mix_identity,
),
_ => Err(ContractError::MaintenanceMode),
// ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
// try_update_rewarding_validator_address(deps, info, address)
// }
// ExecuteMsg::InitEpoch {} => try_init_epoch(info, deps.storage, env),
// ExecuteMsg::BondMixnode {
// mix_node,
// owner_signature,
// } => crate::mixnodes::transactions::try_add_mixnode(
// deps,
// env,
// info,
// mix_node,
// owner_signature,
// ),
// ExecuteMsg::UnbondMixnode {} => {
// crate::mixnodes::transactions::try_remove_mixnode(&env, deps.storage, deps.api, info)
// }
// ExecuteMsg::UpdateMixnodeConfig {
// profit_margin_percent,
// } => crate::mixnodes::transactions::try_update_mixnode_config(
// deps,
// env,
// info,
// profit_margin_percent,
// ),
// ExecuteMsg::UpdateMixnodeConfigOnBehalf {
// profit_margin_percent,
// owner,
// } => crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
// deps,
// env,
// info,
// profit_margin_percent,
// owner,
// ),
// ExecuteMsg::BondGateway {
// gateway,
// owner_signature,
// } => crate::gateways::transactions::try_add_gateway(
// deps,
// env,
// info,
// gateway,
// owner_signature,
// ),
// ExecuteMsg::UnbondGateway {} => {
// crate::gateways::transactions::try_remove_gateway(deps, info)
// }
// ExecuteMsg::UpdateContractStateParams(params) => {
// crate::mixnet_contract_settings::transactions::try_update_contract_settings(
// deps, info, params,
// )
// }
// ExecuteMsg::RewardMixnode { identity, params } => {
// crate::rewards::transactions::try_reward_mixnode(deps, env, info, identity, params)
// }
// ExecuteMsg::DelegateToMixnode { mix_identity } => {
// crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
// }
// ExecuteMsg::UndelegateFromMixnode { mix_identity } => {
// crate::delegations::transactions::try_remove_delegation_from_mixnode(
// deps,
// env,
// info,
// mix_identity,
// )
// }
// // ExecuteMsg::RewardNextMixDelegators {
// // mix_identity,
// // interval_id,
// // } => crate::rewards::transactions::try_reward_next_mixnode_delegators(
// // deps,
// // info,
// // mix_identity,
// // interval_id,
// // ),
// ExecuteMsg::DelegateToMixnodeOnBehalf {
// mix_identity,
// delegate,
// } => crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
// deps,
// env,
// info,
// mix_identity,
// delegate,
// ),
// ExecuteMsg::UndelegateFromMixnodeOnBehalf {
// mix_identity,
// delegate,
// } => crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
// deps,
// env,
// info,
// mix_identity,
// delegate,
// ),
// ExecuteMsg::BondMixnodeOnBehalf {
// mix_node,
// owner,
// owner_signature,
// } => crate::mixnodes::transactions::try_add_mixnode_on_behalf(
// deps,
// env,
// info,
// mix_node,
// owner,
// owner_signature,
// ),
// ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
// crate::mixnodes::transactions::try_remove_mixnode_on_behalf(
// &env,
// deps.storage,
// deps.api,
// info,
// owner,
// )
// }
// ExecuteMsg::BondGatewayOnBehalf {
// gateway,
// owner,
// owner_signature,
// } => crate::gateways::transactions::try_add_gateway_on_behalf(
// deps,
// env,
// info,
// gateway,
// owner,
// owner_signature,
// ),
// ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
// crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
// }
// ExecuteMsg::WriteRewardedSet {
// rewarded_set,
// expected_active_set_size,
// } => crate::interval::transactions::try_write_rewarded_set(
// deps,
// env,
// info,
// rewarded_set,
// expected_active_set_size,
// ),
// ExecuteMsg::AdvanceCurrentEpoch {} => crate::interval::transactions::try_advance_epoch(
// env,
// deps.storage,
// info.sender.to_string(),
// ),
// ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
// crate::rewards::transactions::try_compound_delegator_reward(
// deps,
// env,
// info,
// mix_identity,
// )
// }
// ExecuteMsg::CompoundOperatorReward {} => {
// crate::rewards::transactions::try_compound_operator_reward(deps, env, info)
// }
// ExecuteMsg::CompoundDelegatorRewardOnBehalf {
// owner,
// mix_identity,
// } => crate::rewards::transactions::try_compound_delegator_reward_on_behalf(
// deps,
// env,
// info,
// owner,
// mix_identity,
// ),
// ExecuteMsg::CompoundOperatorRewardOnBehalf { owner } => {
// crate::rewards::transactions::try_compound_operator_reward_on_behalf(
// deps, env, info, owner,
// )
// }
//
// ExecuteMsg::CheckpointMixnodes {} => {
// crate::mixnodes::transactions::try_checkpoint_mixnodes(
// deps.storage,
// env.block.height,
// info,
// )
// }
// ExecuteMsg::ClaimOperatorReward {} => {
// crate::rewards::transactions::try_claim_operator_reward(deps, &env, &info)
// }
// ExecuteMsg::ClaimOperatorRewardOnBehalf { owner } => {
// crate::rewards::transactions::try_claim_operator_reward_on_behalf(
// deps, &env, &info, owner,
// )
// }
// ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
// crate::rewards::transactions::try_claim_delegator_reward(
// deps,
// &env,
// &info,
// &mix_identity,
// )
// }
// ExecuteMsg::ClaimDelegatorRewardOnBehalf {
// mix_identity,
// owner,
// } => crate::rewards::transactions::try_claim_delegator_reward_on_behalf(
// deps,
// &env,
// &info,
// owner,
// &mix_identity,
// ),
}
}
@@ -509,17 +514,7 @@ fn remove_malicious_node(
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
migrate_config_from_env(deps.storage, &msg)?;
let mut response = Response::new();
for node in msg.nodes_to_remove().iter() {
let mut sub_response = remove_malicious_node(deps.storage, deps.api, &env, node)
.unwrap_or_else(|_| panic!("Could not remove node: {:?}", node));
response.messages.append(&mut sub_response.messages);
response.attributes.append(&mut sub_response.attributes);
response.events.append(&mut sub_response.events);
}
Ok(response)
v2_migration(deps, env, msg)
}
#[cfg(test)]
+3
View File
@@ -185,4 +185,7 @@ pub enum ContractError {
#[error("Mixnode {identity} has been blacklisted on the network")]
MixnodeBlacklisted { identity: String },
#[error("Contract is currently set to the maintenance mode - all transactions are temporarily disabled")]
MaintenanceMode,
}
+246 -27
View File
@@ -1,37 +1,256 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Response, Storage};
use cw_storage_plus::Item;
use mixnet_contract_common::{ContractStateParams, MigrateMsg};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::delegations::queries::query_mixnode_delegation;
use crate::delegations::storage::delegations;
use crate::error::ContractError;
use crate::mixnet_contract_settings::models::ContractState;
use crate::mixnet_contract_settings::storage::CONTRACT_STATE;
use crate::mixnodes::storage::mixnodes;
use cosmwasm_std::{wasm_execute, Addr, BankMsg, DepsMut, Env, Response, SubMsg};
use cw_storage_plus::Map;
use mixnet_contract_common::delegation::generate_storage_key;
use mixnet_contract_common::{IdentityKey, MigrateMsg, SpecialV2ExecuteMsg, V2MigrationOperation};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn migrate_config_from_env(
storage: &mut dyn Storage,
msg: &MigrateMsg,
) -> Result<Response, ContractError> {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct OldContractState {
pub owner: Addr,
pub rewarding_validator_address: Addr,
pub params: ContractStateParams,
const MIGRATED_MIXNODES: Map<IdentityKey, u8> = Map::new("migrated-mixnodes");
type OwnerXorProxy = Vec<u8>;
const MIGRATED_DELEGATES: Map<(IdentityKey, OwnerXorProxy), u8> = Map::new("migrated-delegates");
fn migrate_operator(
deps: &mut DepsMut,
v2_mixnet_contract: &str,
node_identity: String,
response: &mut Response,
) -> Result<(), ContractError> {
if MIGRATED_MIXNODES.has(deps.storage, node_identity.clone()) {
// again, panic here because this should never occur and it's incredible dangerous to let it happen
panic!("mixnode {} has already been migrated!", node_identity);
}
const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new("config");
MIGRATED_MIXNODES.save(deps.storage, node_identity.clone(), &1u8)?;
let old_state = OLD_CONTRACT_STATE.load(storage)?;
let new_state = ContractState {
owner: old_state.owner,
mix_denom: msg.mixnet_denom.clone(),
rewarding_validator_address: old_state.rewarding_validator_address,
params: old_state.params,
let bond = mixnodes()
.load(deps.storage, &node_identity)
.expect("failed to read mixnode bond");
let pledge = bond.pledge_amount.clone();
let v2_message = SpecialV2ExecuteMsg::ToBeNamedSaveMixnode {
host: bond.mix_node.host,
mix_port: bond.mix_node.mix_port,
verloc_port: bond.mix_node.verloc_port,
http_api_port: bond.mix_node.http_api_port,
sphinx_key: bond.mix_node.sphinx_key,
identity_key: bond.mix_node.identity_key,
version: bond.mix_node.version,
pledge_amount: bond.pledge_amount,
owner: bond.owner,
block_height: bond.block_height,
proxy: bond.proxy,
};
CONTRACT_STATE.save(storage, &new_state)?;
Ok(Default::default())
// TODO: do we need separate BankMsg here, or can we just use 'funds' here directly?
let wasm_msg = wasm_execute(v2_mixnet_contract, &v2_message, vec![pledge])
.expect("failed to serialize mixnode migration msg");
response.messages.push(SubMsg::new(wasm_msg));
Ok(())
}
fn is_proxy_vesting(proxy: Option<&Addr>, vesting_contract: &str) -> bool {
if let Some(proxy) = proxy {
if proxy.as_ref() == vesting_contract {
return true;
}
}
false
}
fn migrate_delegator(
deps: &mut DepsMut,
v2_mixnet_contract: &str,
vesting_contract: &str,
address: Addr,
node_identity: String,
proxy: Option<Addr>,
new_mix_id: Option<u64>,
response: &mut Response,
) -> Result<(), ContractError> {
let owner_proxy = generate_storage_key(&address, proxy.as_ref());
let storage_key = (node_identity.clone(), owner_proxy);
if MIGRATED_DELEGATES.has(deps.storage, storage_key.clone()) {
// again, panic here because this should never occur and it's incredible dangerous to let it happen
panic!(
"delegator {}/{} has already been migrated!",
address, node_identity
);
}
MIGRATED_DELEGATES.save(deps.storage, storage_key, &1u8)?;
// there should only be one (as we ensured it during previous migration steps, if not, then somebody is not following the migration instructions
// and we're in an inconsistent state)
// also this entry MUST exist as we're explicitly migrating this one
let mut delegation = query_mixnode_delegation(
deps.storage,
deps.api,
node_identity.clone(),
address.clone().into_string(),
proxy.map(Addr::into_string),
)
.expect("specified delegation doesn't exist!!");
if delegation.len() != 1 {
panic!("the universal compound hasn't been run prior to this migration!!")
}
// take ownership of the one and only entry
let delegation = delegation.pop().unwrap();
// if mix_id is `None`, it means target mixnode doesn't exist anymore -> return the tokens
// otherwise attempt to migrate it into the new contract
if let Some(migrated_mix_id) = new_mix_id {
if is_proxy_vesting(delegation.proxy.as_ref(), vesting_contract) {
let vesting_update = VestingContractExecuteMsg::AuthorisedUpdateToV2 {
owner: address.into_string(),
node_identity,
mix_id: migrated_mix_id,
};
let wasm_msg = wasm_execute(vesting_contract, &vesting_update, vec![])
.expect("failed to serialize vesting migration msg");
response.messages.push(SubMsg::new(wasm_msg));
}
let stake = delegation.amount.clone();
let v2_message = SpecialV2ExecuteMsg::ToBeNamedSaveDelegation {
owner: delegation.owner,
mix_id: migrated_mix_id,
amount: delegation.amount,
block_height: delegation.block_height,
proxy: delegation.proxy,
};
let wasm_msg = wasm_execute(v2_mixnet_contract, &v2_message, vec![stake])
.expect("failed to serialize mixnode migration msg");
response.messages.push(SubMsg::new(wasm_msg));
} else {
let mut to_address = delegation.owner.to_string();
let mut make_bank_msg = true;
// if the specified proxy matches the vesting contract address -> treat it as "proper" undelegation
// and send tokens back there
if let Some(proxy) = delegation.proxy {
to_address = proxy.to_string();
if proxy == vesting_contract {
make_bank_msg = false;
let vesting_track = VestingContractExecuteMsg::TrackUndelegation {
owner: delegation.owner.to_string(),
mix_identity: delegation.node_identity,
amount: delegation.amount.clone(),
};
let wasm_msg = wasm_execute(
vesting_contract,
&vesting_track,
vec![delegation.amount.clone()],
)?;
response.messages.push(SubMsg::new(wasm_msg));
}
}
// otherwise just send tokens back to the user / different proxy
if make_bank_msg {
let return_tokens = BankMsg::Send {
to_address,
amount: vec![delegation.amount],
};
response.messages.push(SubMsg::new(return_tokens));
}
}
Ok(())
}
fn remove_operator(
deps: &mut DepsMut,
env: &Env,
node_identity: String,
) -> Result<(), ContractError> {
if !MIGRATED_MIXNODES.has(deps.storage, node_identity.clone()) {
// again, panic here because this should never occur and it's incredible dangerous to let it happen
panic!(
"attempted to remove mixnode {} without prior migration!",
node_identity
);
}
mixnodes().remove(deps.storage, &node_identity, env.block.height)?;
Ok(())
}
fn remove_delegator(
deps: &mut DepsMut,
address: Addr,
node_identity: String,
proxy: Option<Addr>,
) -> Result<(), ContractError> {
let owner_proxy = generate_storage_key(&address, proxy.as_ref());
let storage_key = (node_identity.clone(), owner_proxy);
if !MIGRATED_DELEGATES.has(deps.storage, storage_key.clone()) {
// again, panic here because this should never occur and it's incredible dangerous to let it happen
panic!(
"attempted to remove delegator {}/{} without prior migration!",
address, node_identity
);
}
let height = delegations()
.prefix(storage_key.clone())
.keys(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.next()
.unwrap()
.unwrap();
delegations().remove(deps.storage, (storage_key.0, storage_key.1, height))?;
Ok(())
}
pub fn v2_migration(
mut deps: DepsMut<'_>,
env: Env,
msg: MigrateMsg,
) -> Result<Response, ContractError> {
let mut response = Response::new();
// note: we're explicitly failing on failures here because we expect EVERYTHING we migrate to exist
for op in msg.operations {
match op {
V2MigrationOperation::MigrateOperator { node_identity } => {
migrate_operator(
&mut deps,
&msg.v2_contract_address,
node_identity,
&mut response,
)?;
}
V2MigrationOperation::MigrateDelegator {
address,
node_identity,
proxy,
new_mix_id,
} => migrate_delegator(
&mut deps,
&msg.v2_contract_address,
&msg.vesting_contract_address,
address,
node_identity,
proxy,
new_mix_id,
&mut response,
)?,
V2MigrationOperation::RemoveOperator { node_identity } => {
remove_operator(&mut deps, &env, node_identity)?;
}
V2MigrationOperation::RemoveDelegator {
address,
node_identity,
proxy,
} => remove_delegator(&mut deps, address, node_identity, proxy)?,
}
}
Ok(response)
}