Files
nym/mixnode/src/commands/sign.rs
T
Pierre Dommerc 7ff043d8df Feature/bonding signature UI (#3157)
* wip

* updated gateways 'sign' command

* in-wallet verification of mix bonding signature

* changed signature of vesting contract trait method

* updated wallet bonding endpoints

* tauri commands for generating signing payloads

* renamed signer to sender

* verifying new signatures in the contract

* fixed existing mixnet unit tests

* unit tests for invalid signatures

* fixed other usages of MessageSignature + FromStr

* using base58-encoded serialization

* removed owner-signature from details response

* added ability to construct bonding payloads via nym-cli

* removed signature from bonding payload args

* moved 'message_type' from 'ContractMessageContent' to 'SignableMessage'

* refactor(wallet-rust): rename owner_signature args

* feat(wallet-bonding): handle user signature

* feat(wallet-bonding): fix bonding

* feat(wallet-bonding): fix lint issue

* feat(wallet-bonding): ui adjustment

* make the location field mandatory for payload signing

* feat(wallet-bonding): remove ownersignature field, remove dead code

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2023-03-10 15:53:21 +01:00

141 lines
4.9 KiB
Rust

// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
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::{bail, Result};
use clap::{ArgGroup, Args};
use log::error;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use validator_client::nyxd;
use super::version_check;
#[derive(Args, Clone)]
#[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)]
id: String,
/// Signs your blockchain address with your identity key
// the alias here is included for backwards compatibility (1.1.4 and before)
#[clap(long, alias = "address")]
wallet_address: Option<nyxd::AccountId>,
/// 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 {
type Error = anyhow::Error;
fn try_from(args: Sign) -> Result<Self, Self::Error> {
if let Some(text) = args.text {
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
bail!("Error: missing signed target flag")
}
}
}
fn print_signed_address(private_key: &identity::PrivateKey, wallet_address: nyxd::AccountId) {
// perform extra validation to ensure we have correct prefix
validate_bech32_address_or_exit(wallet_address.as_ref());
let signature = private_key.sign_text(wallet_address.as_ref());
println!("The base58-encoded signature on '{wallet_address}' is: {signature}",);
}
fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
println!("Signing the text {text:?} using your mixnode's Ed25519 identity key...");
let signature = private_key.sign_text(text);
println!("The base58-encoded signature on '{text}' is: {signature}");
}
fn print_signed_contract_msg(private_key: &identity::PrivateKey, raw_msg: &str) {
let trimmed = raw_msg.trim();
println!(">>> attempting to sign {trimmed}");
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
return;
};
println!(">>> decoding the message...");
// 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 bs58 encoded json
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
return;
};
// if this is a valid json, it MUST be a valid string
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
println!(">>> message to sign: {decoded_string}");
let signature = private_key.sign(&decoded).to_base58_string();
println!(">>> The base58-encoded signature is:\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: {err})",
args.id,
);
return;
}
};
if !version_check(&config) {
error!("Failed the local version check");
return;
}
let signed_target = match SignedTarget::try_from(args.clone()) {
Ok(s) => s,
Err(err) => {
error!("{err}");
return;
}
};
let pathfinder = MixNodePathfinder::new_from_config(&config);
let identity_keypair = MixNode::load_identity_keys(&pathfinder);
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)
}
}
}