introduced specialised subcommand to importer-cli to import mixnet/vesting contracts

This commit is contained in:
Jędrzej Stuczyński
2024-10-11 17:34:37 +01:00
parent 67462a9f47
commit e333aca8a1
5 changed files with 257 additions and 4 deletions
Generated
+2
View File
@@ -3315,8 +3315,10 @@ dependencies = [
"dirs 5.0.1",
"importer-contract",
"nym-bin-common",
"nym-mixnet-contract-common",
"nym-network-defaults",
"nym-validator-client",
"nym-vesting-contract-common",
"serde",
"serde_json",
"tokio",
@@ -24,3 +24,6 @@ importer-contract = { path = "../importer-contract" }
nym-validator-client = { path = "../../../../common/client-libs/validator-client" }
nym-bin-common = { path = "../../../../common/bin-common", features = ["basic_tracing"] }
nym-network-defaults = { path = "../../../../common/network-defaults" }
nym-mixnet-contract-common = { path = "../../../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../../../../common/cosmwasm-smart-contracts/vesting-contract" }
@@ -1,4 +1,163 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use clap::ArgGroup;
use crate::commands::prepare::create_importer_contract;
use crate::commands::set_state::set_importer_state;
use crate::commands::swap_contract::swap_contract;
use crate::helpers::{mixnet_contract_path, vesting_contract_path};
use clap::Args;
use importer_contract::ExecuteMsg;
use nym_mixnet_contract_common::{Addr, ContractState};
use nym_validator_client::nyxd::{AccountId, CosmWasmClient};
use nym_validator_client::DirectSigningHttpRpcNyxdClient;
use serde::Serialize;
use std::path::PathBuf;
use tracing::info;
#[derive(Args, Clone)]
pub struct InitialiseMixnetVestingWithStatesArgs {
/// Path to the .wasm file with the importer contract
/// If not provided, the CLI will attempt to traverse the parent directories until it finds
/// "target/wasm32-unknown-unknown/release/importer_contract.wasm"
#[clap(long)]
pub importer_contract_path: Option<PathBuf>,
/// Path to the file containing state dump of the mixnet contract
#[clap(long)]
pub mixnet_state: PathBuf,
/// Path to the file containing state dump of the vesting contract
#[clap(long)]
pub vesting_state: PathBuf,
/// Path to mixnet contract that will be uploaded and applied to the imported state
/// If not provided, the CLI will attempt to traverse the parent directories until it finds
/// "target/wasm32-unknown-unknown/release/mixnet_contract.wasm"
#[clap(long)]
pub mixnet_contract_path: Option<PathBuf>,
/// Path to the vesting contract that will be uploaded and applied to the imported state
/// If not provided, the CLI will attempt to traverse the parent directories until it finds
/// "target/wasm32-unknown-unknown/release/vesting_contract.wasm"
#[clap(long)]
pub vesting_contract_path: Option<PathBuf>,
}
pub async fn update_kv<T>(
client: &DirectSigningHttpRpcNyxdClient,
contract: &AccountId,
key: &[u8],
value: &T,
) -> anyhow::Result<()>
where
T: Serialize,
{
let msg = ExecuteMsg::from(vec![(key.to_vec(), serde_json::to_vec(value)?)]);
client
.execute(contract, &msg, None, "updating kv...", Vec::new())
.await?;
Ok(())
}
#[allow(deprecated)]
pub async fn execute_initialise_mixnet_vesting_with_states(
args: InitialiseMixnetVestingWithStatesArgs,
client: DirectSigningHttpRpcNyxdClient,
) -> anyhow::Result<()> {
let mixnet_contract = mixnet_contract_path(args.mixnet_contract_path)?;
let vesting_contract = vesting_contract_path(args.vesting_contract_path)?;
info!(
"using the following mixnet contract: {}",
mixnet_contract.display()
);
info!(
"using the following vesting contract: {}",
vesting_contract.display()
);
let admin = client.address();
// 1. import mixnet state
info!("importing the mixnet contract state...");
let mixnet_importer_address =
create_importer_contract(args.importer_contract_path.clone(), &client).await?;
set_importer_state(
args.mixnet_state,
Some(mixnet_importer_address.clone()),
&client,
)
.await?;
// 2. import vesting state
info!("importing the vesting contract state...");
let vesting_importer_address =
create_importer_contract(args.importer_contract_path, &client).await?;
set_importer_state(
args.vesting_state,
Some(vesting_importer_address.clone()),
&client,
)
.await?;
// 3. adjust few state entries
// update vesting admin:
info!("adjusting internal contract states...");
let new_vesting_admin = Addr::unchecked(admin.as_ref());
update_kv(
&client,
&vesting_importer_address,
b"adm",
&new_vesting_admin,
)
.await?;
// update mixnet contract address in the vesting contract
let new_mixnet_contract_address = Addr::unchecked(mixnet_importer_address.as_ref());
update_kv(
&client,
&vesting_importer_address,
b"mix",
&new_mixnet_contract_address,
)
.await?;
// update admins and vesting contract addresses in the mixnet contract
let raw_contract_state = client
.query_contract_raw(&mixnet_importer_address, b"state".to_vec())
.await?;
let mut contract_state: ContractState = serde_json::from_slice(&raw_contract_state)?;
contract_state.vesting_contract_address = Addr::unchecked(vesting_importer_address.as_ref());
contract_state.owner = Some(Addr::unchecked(admin.as_ref()));
contract_state.rewarding_validator_address = Addr::unchecked(admin.as_ref());
update_kv(&client, &mixnet_importer_address, b"state", &contract_state).await?;
let contract_admin = Some(Addr::unchecked(admin.as_ref()));
update_kv(&client, &mixnet_importer_address, b"admin", &contract_admin).await?;
// 4. apply the correct contract codes
info!("swapping the mixnet contract to the correct .wasm...");
swap_contract(
None,
Some(mixnet_contract),
Some(mixnet_importer_address.clone()),
None,
&client,
)
.await?;
info!("swapping the vesting contract to the correct .wasm...");
swap_contract(
None,
Some(vesting_contract),
Some(vesting_importer_address.clone()),
None,
&client,
)
.await?;
info!("the contracts are ready");
info!("MIXNET: {mixnet_importer_address} VESTING: {vesting_importer_address}");
Ok(())
}
@@ -1,6 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::initialise_mixnet_vesting_with_states::{
execute_initialise_mixnet_vesting_with_states, InitialiseMixnetVestingWithStatesArgs,
};
use crate::commands::initialise_with_state::{initialise_with_state, InitialiseWithStateArgs};
use crate::commands::prepare::{execute_prepare_contract, PrepareArgs};
use crate::commands::set_state::{execute_set_state, SetStateArgs};
@@ -50,6 +53,10 @@ pub(crate) enum Commands {
/// Combines the functionalities of `prepare-contract`, `set-state` and `swap-contract`
InitialiseWithState(InitialiseWithStateArgs),
/// Specialised variant of `initialise-with-state` for the mixnet and vesting contracts that sets
/// internal state keys
InitialiseMixnetVestingWithStates(InitialiseMixnetVestingWithStatesArgs),
}
impl Cli {
@@ -73,6 +80,9 @@ impl Cli {
Commands::SetState(args) => execute_set_state(args, client).await,
Commands::SwapContract(args) => execute_swap_contract(args, client).await,
Commands::InitialiseWithState(args) => initialise_with_state(args, client).await,
Commands::InitialiseMixnetVestingWithStates(args) => {
execute_initialise_mixnet_vesting_with_states(args, client).await
}
}
}
}
@@ -3,15 +3,63 @@
use crate::state::CachedState;
use anyhow::bail;
use nym_validator_client::nyxd::cosmwasm_client::types::Model;
use nym_validator_client::nyxd::AccountId;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env::current_dir;
use std::fs;
use std::path::PathBuf;
use tracing::debug;
pub fn importer_contract_path(explicit: Option<PathBuf>) -> anyhow::Result<PathBuf> {
contract_path("importer_contract.wasm", explicit)
}
pub fn mixnet_contract_path(explicit: Option<PathBuf>) -> anyhow::Result<PathBuf> {
contract_path("mixnet_contract.wasm", explicit)
}
pub fn vesting_contract_path(explicit: Option<PathBuf>) -> anyhow::Result<PathBuf> {
contract_path("vesting_contract.wasm", explicit)
}
fn find_contract_in(root: PathBuf, target_name: &str) -> anyhow::Result<Option<PathBuf>> {
for content in root.read_dir()? {
let dir_entry = content?;
let path = dir_entry.path();
debug!("checking {:?}", fs::canonicalize(path.clone()));
let Ok(name) = dir_entry.file_name().into_string() else {
continue;
};
if name == "target" {
let maybe_contract_path = dir_entry
.path()
.join("wasm32-unknown-unknown")
.join("release")
.join(target_name);
if maybe_contract_path.exists() {
return Ok(Some(maybe_contract_path));
}
}
if path.is_dir() {
if let Some(found) = find_contract_in(path, target_name)? {
return Ok(Some(found));
}
}
}
Ok(None)
}
// this only works if the cli is called from somewhere within the nym directory
// (which realistically is going to be the case most of the time)
pub fn importer_contract_path(explicit: Option<PathBuf>) -> anyhow::Result<PathBuf> {
pub fn contract_path(target_name: &str, explicit: Option<PathBuf>) -> anyhow::Result<PathBuf> {
if let Some(explicit) = explicit {
return Ok(explicit);
}
@@ -29,16 +77,24 @@ pub fn importer_contract_path(explicit: Option<PathBuf>) -> anyhow::Result<PathB
.path()
.join("wasm32-unknown-unknown")
.join("release")
.join("importer_contract.wasm");
.join(target_name);
if maybe_contract_path.exists() {
return Ok(maybe_contract_path);
}
}
// SPECIAL CASE:
// if there's a `contracts` directory, do traverse its children
if name == "contracts" {
if let Some(contract) = find_contract_in(dir_entry.path(), target_name)? {
return Ok(contract);
}
}
}
}
bail!("could not find importer_contract.wasm")
bail!("could not find {target_name}")
}
pub fn importer_contract_address(explicit: Option<AccountId>) -> anyhow::Result<AccountId> {
@@ -49,3 +105,26 @@ pub fn importer_contract_address(explicit: Option<AccountId>) -> anyhow::Result<
let state = CachedState::load()?;
Ok(state.importer_address)
}
pub fn find_value<T>(raw_state: &[Model], key: &[u8]) -> anyhow::Result<Option<T>>
where
T: DeserializeOwned,
{
let Some(entry) = raw_state.iter().find(|kv| kv.key == key) else {
return Ok(None);
};
Ok(Some(serde_json::from_slice(&entry.value)?))
}
pub fn update_value<T>(raw_state: &mut [Model], key: &[u8], value: &T) -> anyhow::Result<()>
where
T: Serialize,
{
let Some(entry) = raw_state.iter_mut().find(|kv| kv.key == key) else {
bail!("couldn't find corresponding state entry")
};
entry.value = serde_json::to_vec(value)?;
Ok(())
}