This commit is contained in:
Jędrzej Stuczyński
2023-02-22 17:18:37 +00:00
parent c904d245d2
commit aa9609ec6d
13 changed files with 309 additions and 37 deletions
Generated
+1
View File
@@ -3507,6 +3507,7 @@ dependencies = [
"mixnode-common",
"nym-bin-common",
"nym-config",
"nym-contracts-common",
"nym-crypto",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
@@ -6,6 +6,7 @@
pub mod dealings;
pub mod events;
pub mod signing;
pub mod types;
pub use types::*;
@@ -0,0 +1,131 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{from_slice, to_vec, Addr, Coin, MessageInfo, StdResult};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
pub type Nonce = u32;
#[derive(Serialize)]
#[serde(transparent)]
pub struct MessageType(String);
impl<T> From<T> for MessageType
where
T: ToString,
{
fn from(value: T) -> Self {
MessageType(value.to_string())
}
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SigningAlgorithm {
#[default]
Ed25519,
Secp256k1,
}
// TODO: maybe move this one to repo-wide common?
#[derive(Serialize, Deserialize)]
pub struct SignableMessage<T> {
nonce: u32,
algorithm: SigningAlgorithm,
content: T,
}
impl<T> SignableMessage<T> {
pub fn new(nonce: u32, content: T) -> Self {
SignableMessage {
nonce,
algorithm: SigningAlgorithm::Ed25519,
content,
}
}
pub fn with_signing_algorithm(mut self, algorithm: SigningAlgorithm) -> Self {
self.algorithm = algorithm;
self
}
pub fn to_plaintext(&self) -> StdResult<Vec<u8>>
where
T: Serialize,
{
to_vec(self)
}
pub fn to_string(&self) -> StdResult<String>
where
T: Serialize,
{
// if you look into implementation of `serde_json_wasm::to_string` this [i.e. the String conversion]
// CAN'T fail, but let's avoid this unnecessary unwrap either way
self.to_plaintext()
.map(|s| String::from_utf8(s).unwrap_or(String::from("SERIALIZATION FAILURE")))
}
pub fn try_from_bytes(bytes: &[u8]) -> StdResult<SignableMessage<T>>
where
T: DeserializeOwned,
{
from_slice(bytes)
}
pub fn try_from_string(raw: &str) -> StdResult<SignableMessage<T>>
where
T: DeserializeOwned,
{
Self::try_from_bytes(raw.as_bytes())
}
}
#[derive(Serialize)]
struct ContractMessageContent<T> {
message_type: MessageType,
signer: Addr,
proxy: Option<Addr>,
funds: Vec<Coin>,
data: T,
}
impl<T> ContractMessageContent<T> {
pub fn new(
message_type: MessageType,
signer: Addr,
proxy: Option<Addr>,
funds: Vec<Coin>,
data: T,
) -> Self {
ContractMessageContent {
message_type,
signer,
proxy,
funds,
data,
}
}
pub fn new_with_info(
message_type: MessageType,
info: MessageInfo,
signer: Addr,
data: T,
) -> Self {
let proxy = if info.sender == signer {
None
} else {
Some(info.sender)
};
ContractMessageContent {
message_type,
signer,
proxy,
funds: info.funds,
data,
}
}
}
@@ -10,6 +10,7 @@ use crate::reward_params::{
};
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
use crate::{Gateway, IdentityKey, MixNode};
use contracts_common::signing::MessageType;
use cosmwasm_std::Decimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -240,6 +241,54 @@ pub enum ExecuteMsg {
}
impl ExecuteMsg {
pub fn message_type(&self) -> MessageType {
match self {
ExecuteMsg::AssignNodeLayer { .. } => {}
ExecuteMsg::CreateFamily { .. } => {}
ExecuteMsg::JoinFamily { .. } => {}
ExecuteMsg::LeaveFamily { .. } => {}
ExecuteMsg::KickFamilyMember { .. } => {}
ExecuteMsg::CreateFamilyOnBehalf { .. } => {}
ExecuteMsg::JoinFamilyOnBehalf { .. } => {}
ExecuteMsg::LeaveFamilyOnBehalf { .. } => {}
ExecuteMsg::KickFamilyMemberOnBehalf { .. } => {}
ExecuteMsg::UpdateRewardingValidatorAddress { .. } => {}
ExecuteMsg::UpdateContractStateParams { .. } => {}
ExecuteMsg::UpdateActiveSetSize { .. } => {}
ExecuteMsg::UpdateRewardingParams { .. } => {}
ExecuteMsg::UpdateIntervalConfig { .. } => {}
ExecuteMsg::BeginEpochTransition => {}
ExecuteMsg::AdvanceCurrentEpoch { .. } => {}
ExecuteMsg::ReconcileEpochEvents { .. } => {}
ExecuteMsg::BondMixnode { .. } => {}
ExecuteMsg::BondMixnodeOnBehalf { .. } => {}
ExecuteMsg::PledgeMore { .. } => {}
ExecuteMsg::PledgeMoreOnBehalf { .. } => {}
ExecuteMsg::UnbondMixnode { .. } => {}
ExecuteMsg::UnbondMixnodeOnBehalf { .. } => {}
ExecuteMsg::UpdateMixnodeCostParams { .. } => {}
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. } => {}
ExecuteMsg::UpdateMixnodeConfig { .. } => {}
ExecuteMsg::UpdateMixnodeConfigOnBehalf { .. } => {}
ExecuteMsg::BondGateway { .. } => {}
ExecuteMsg::BondGatewayOnBehalf { .. } => {}
ExecuteMsg::UnbondGateway { .. } => {}
ExecuteMsg::UnbondGatewayOnBehalf { .. } => {}
ExecuteMsg::DelegateToMixnode { .. } => {}
ExecuteMsg::DelegateToMixnodeOnBehalf { .. } => {}
ExecuteMsg::UndelegateFromMixnode { .. } => {}
ExecuteMsg::UndelegateFromMixnodeOnBehalf { .. } => {}
ExecuteMsg::RewardMixnode { .. } => {}
ExecuteMsg::WithdrawOperatorReward { .. } => {}
ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. } => {}
ExecuteMsg::WithdrawDelegatorReward { .. } => {}
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { .. } => {}
#[cfg(feature = "contract-testing")]
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {}
}
todo!()
}
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::AssignNodeLayer { mix_id, layer } => {
+1 -2
View File
@@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
#nym-config = { path = "../../common/config"}
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.2.0" }
cosmwasm-std = "1.0.0"
cosmwasm-storage = "1.0.0"
@@ -41,7 +41,6 @@ semver = { version = "1.0.16", default-features = false }
[dev-dependencies]
cosmwasm-schema = "1.0.0"
rand_chacha = "0.2"
#rand = "0.7"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
[build-dependencies]
+32 -30
View File
@@ -39,41 +39,43 @@ pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10;
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
// storage keys
pub(crate) const DELEGATION_PK_NAMESPACE: &str = "dl";
pub(crate) const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
pub(crate) const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
pub const DELEGATION_PK_NAMESPACE: &str = "dl";
pub const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
pub const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
pub(crate) const GATEWAYS_PK_NAMESPACE: &str = "gt";
pub(crate) const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
pub const GATEWAYS_PK_NAMESPACE: &str = "gt";
pub const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
pub(crate) const REWARDED_SET_KEY: &str = "rs";
pub(crate) const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
pub(crate) const CURRENT_INTERVAL_KEY: &str = "ci";
pub(crate) const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
pub(crate) const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
pub(crate) const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
pub(crate) const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
pub const REWARDED_SET_KEY: &str = "rs";
pub const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
pub const CURRENT_INTERVAL_KEY: &str = "ci";
pub const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
pub const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
pub const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
pub const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
pub(crate) const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
pub(crate) const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub(crate) const CONTRACT_STATE_KEY: &str = "state";
pub const CONTRACT_STATE_KEY: &str = "state";
pub(crate) const LAYER_DISTRIBUTION_KEY: &str = "layers";
pub(crate) const NODE_ID_COUNTER_KEY: &str = "nic";
pub(crate) const MIXNODES_PK_NAMESPACE: &str = "mnn";
pub(crate) const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
pub(crate) const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
pub(crate) const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
pub const LAYER_DISTRIBUTION_KEY: &str = "layers";
pub const NODE_ID_COUNTER_KEY: &str = "nic";
pub const MIXNODES_PK_NAMESPACE: &str = "mnn";
pub const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
pub const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
pub const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
pub(crate) const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
pub(crate) const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
pub(crate) const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
pub const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
pub const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
pub const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
pub(crate) const REWARDING_PARAMS_KEY: &str = "rparams";
pub(crate) const PENDING_REWARD_POOL_KEY: &str = "prp";
pub(crate) const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
pub const REWARDING_PARAMS_KEY: &str = "rparams";
pub const PENDING_REWARD_POOL_KEY: &str = "prp";
pub const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2";
pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2";
pub const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
pub const FAMILIES_MAP_NAMESPACE: &str = "fam2";
pub const MEMBERS_MAP_NAMESPACE: &str = "memb2";
pub const SIGNING_NONCES_NAMESPACE: &str = "sn";
+1
View File
@@ -15,6 +15,7 @@ mod mixnodes;
mod queued_migrations;
mod rewards;
mod support;
mod signing;
#[cfg(feature = "contract-testing")]
mod testing;
+17
View File
@@ -0,0 +1,17 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage;
use cosmwasm_std::{Addr, Storage};
use mixnet_contract_common::error::MixnetContractError;
fn construct_signed_message<T>(
storage: &mut dyn Storage,
signer: Addr,
message: T,
) -> Result<(), MixnetContractError> {
// right now all signing is done with ed25519 so that simplifies things a bit
let nonce = storage::get_and_update_signing_nonce(storage, signer)?;
todo!()
}
+6
View File
@@ -0,0 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod helpers;
pub mod queries;
pub mod storage;
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage::get_signing_nonce;
use cosmwasm_std::{Deps, StdResult};
use nym_contracts_common::signing::Nonce;
pub fn try_get_current_signing_nonce(deps: Deps<'_>, address: String) -> StdResult<Nonce> {
let address = deps.api.addr_validate(&address)?;
get_signing_nonce(deps.storage, address)
}
+32
View File
@@ -0,0 +1,32 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::constants::SIGNING_NONCES_NAMESPACE;
use cosmwasm_std::{Addr, StdResult, Storage};
use cw_storage_plus::Map;
use nym_contracts_common::signing::Nonce;
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult<Nonce> {
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
Ok(nonce)
}
pub fn update_signing_nonce(
storage: &mut dyn Storage,
address: Addr,
value: Nonce,
) -> StdResult<()> {
NONCES.save(storage, address, &value)
}
pub fn get_and_update_signing_nonce(storage: &mut dyn Storage, address: Addr) -> StdResult<Nonce> {
// get the current nonce
let nonce = get_signing_nonce(storage, address.clone())?;
// increment it for the next use
update_signing_nonce(storage, address, nonce + 1)?;
Ok(nonce)
}
+1
View File
@@ -41,6 +41,7 @@ atty = "0.2"
## internal
nym-config = { path="../common/config" }
nym-crypto = { path="../common/crypto" }
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
mixnet-client = { path="../common/client-libs/mixnet-client" }
mixnode-common = { path="../common/mixnode-common" }
nym-nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
+26 -5
View File
@@ -6,7 +6,7 @@ use std::convert::TryFrom;
use crate::commands::validate_bech32_address_or_exit;
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
use crate::node::MixNode;
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
use log::error;
use nym_config::NymConfig;
@@ -16,7 +16,7 @@ use validator_client::nyxd;
use super::version_check;
#[derive(Args, Clone)]
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text"])))]
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))]
pub(crate) struct Sign {
/// The id of the mixnode you want to sign with
#[clap(long)]
@@ -30,11 +30,16 @@ pub(crate) struct Sign {
/// Signs an arbitrary piece of text with your identity key
#[clap(long)]
text: Option<String>,
/// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
#[clap(long)]
contract_msg: Option<String>,
}
enum SignedTarget {
Text(String),
Address(nyxd::AccountId),
ContractMsg(String),
}
impl TryFrom<Sign> for SignedTarget {
@@ -45,11 +50,13 @@ impl TryFrom<Sign> for SignedTarget {
Ok(SignedTarget::Text(text))
} else if let Some(address) = args.wallet_address {
Ok(SignedTarget::Address(address))
} else if let Some(msg) = args.contract_msg {
Ok(SignedTarget::ContractMsg(msg))
} else {
// This is unreachable, and hopefully clap will support it explicitly by outputting an
// enum from the ArgGroup in the future.
// See: https://github.com/clap-rs/clap/issues/2621
Err(anyhow!("Error: missing signed target flag"))
bail!("Error: missing signed target flag")
}
}
}
@@ -70,14 +77,25 @@ fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
println!("The base58-encoded signature on '{text}' is: {signature}");
}
fn print_signed_contract_msg(private_key: &identity::PrivateKey, raw_msg: &str) {
// we don't really care about what particular information is embedded inside of it,
// we just want to know if user correctly copied the string, i.e. whether it's a valid json
if serde_json::from_str::<serde_json::Value>(raw_msg).is_err() {
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters")
} else {
let msg = raw_msg.trim();
let signature = private_key.sign(msg.trim().as_bytes()).to_base58_string();
println!("The base58-encoded signature on\n{msg}\nis:\n{signature}");
}
}
pub(crate) fn execute(args: &Sign) {
let config = match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
error!(
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})",
args.id,
err,
);
return;
}
@@ -101,5 +119,8 @@ pub(crate) fn execute(args: &Sign) {
match signed_target {
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
SignedTarget::Address(addr) => print_signed_address(identity_keypair.private_key(), addr),
SignedTarget::ContractMsg(raw_msg) => {
print_signed_contract_msg(identity_keypair.private_key(), &raw_msg)
}
}
}