mirror of
https://github.com/mimblewimble/mwixnet.git
synced 2025-01-20 19:11:09 +03:00
switch to thiserror for wallet
This commit is contained in:
parent
fdbf25c965
commit
10faae9efe
3 changed files with 87 additions and 38 deletions
16
src/error.rs
16
src/error.rs
|
@ -12,7 +12,7 @@ pub struct Error {
|
|||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// MWixnet error types
|
||||
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
/// Error from secp256k1-zkp library
|
||||
#[fail(display = "Secp Error")]
|
||||
|
@ -20,9 +20,6 @@ pub enum ErrorKind {
|
|||
/// Wraps an io error produced when reading or writing
|
||||
#[fail(display = "IO Error: {}", _0)]
|
||||
IOErr(String, io::ErrorKind),
|
||||
/// Data wasn't in a consumable format
|
||||
#[fail(display = "CorruptedData")]
|
||||
CorruptedData,
|
||||
/// Error from grin's api crate
|
||||
#[fail(display = "GRIN API Error: {}", _0)]
|
||||
GrinApiError(String),
|
||||
|
@ -38,6 +35,9 @@ pub enum ErrorKind {
|
|||
/// Error from invalid signature
|
||||
#[fail(display = "invalid signature")]
|
||||
InvalidSigError,
|
||||
/// Wallet error
|
||||
#[fail(display = "wallet error: {}", _0)]
|
||||
WalletError(crate::wallet::WalletError),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
@ -137,3 +137,11 @@ impl From<serde_json::Error> for Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::wallet::WalletError> for Error {
|
||||
fn from(e: crate::wallet::WalletError) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::WalletError(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,10 @@ pub fn read_secret_key<R: Reader>(reader: &mut R) -> std::result::Result<SecretK
|
|||
}
|
||||
|
||||
/// Build a Pedersen Commitment using the provided value and blinding factor
|
||||
pub fn commit(value: u64, blind: &SecretKey) -> Result<Commitment> {
|
||||
pub fn commit(
|
||||
value: u64,
|
||||
blind: &SecretKey,
|
||||
) -> std::result::Result<Commitment, secp256k1zkp::Error> {
|
||||
let secp = Secp256k1::with_caps(ContextFlag::Commit);
|
||||
let commit = secp.commit(value, blind.clone())?;
|
||||
Ok(commit)
|
||||
|
@ -194,7 +197,7 @@ pub fn sub_value(
|
|||
}
|
||||
|
||||
/// Signs the message with the provided SecretKey
|
||||
pub fn sign(sk: &SecretKey, msg: &Message) -> Result<Signature> {
|
||||
pub fn sign(sk: &SecretKey, msg: &Message) -> std::result::Result<Signature, secp256k1zkp::Error> {
|
||||
let secp = Secp256k1::with_caps(ContextFlag::Full);
|
||||
let pubkey = PublicKey::from_secret_key(&secp, &sk)?;
|
||||
let sig = aggsig::sign_single(&secp, &msg, &sk, None, None, None, Some(&pubkey), None)?;
|
||||
|
|
102
src/wallet.rs
102
src/wallet.rs
|
@ -1,4 +1,3 @@
|
|||
use crate::error::{ErrorKind, Result};
|
||||
use crate::secp;
|
||||
|
||||
use grin_api::client;
|
||||
|
@ -15,10 +14,38 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::json;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub trait Wallet: Send + Sync {
|
||||
/// Builds an output for the wallet with the provided amount.
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)>;
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output), WalletError>;
|
||||
}
|
||||
|
||||
/// Error types for interacting with wallets
|
||||
#[derive(Error, Debug)]
|
||||
pub enum WalletError {
|
||||
#[error("Error building kernel's fee fields: {0:?}")]
|
||||
KernelFeeError(grin_core::core::transaction::Error),
|
||||
#[error("Error computing kernel's excess: {0:?}")]
|
||||
KernelExcessError(secp256k1zkp::Error),
|
||||
#[error("Error computing kernel's signature message: {0:?}")]
|
||||
KernelSigMessageError(grin_core::core::transaction::Error),
|
||||
#[error("Error signing kernel: {0:?}")]
|
||||
KernelSigError(secp256k1zkp::Error),
|
||||
#[error("Built kernel failed to verify: {0:?}")]
|
||||
KernelVerifyError(grin_core::core::transaction::Error),
|
||||
#[error("Output blinding factor is invalid: {0:?}")]
|
||||
OutputBlindError(secp256k1zkp::Error),
|
||||
#[error("Error encrypting request: {0:?}")]
|
||||
EncryptRequestError(grin_wallet_libwallet::Error),
|
||||
#[error("Error decrypting response: {0:?}")]
|
||||
DecryptResponseError(grin_wallet_libwallet::Error),
|
||||
#[error("Error decoding JSON response: {0:?}")]
|
||||
DecodeResponseError(serde_json::Error),
|
||||
#[error("JSON-RPC API communication error: {0:?}")]
|
||||
ApiCommError(grin_api::Error),
|
||||
#[error("Error decoding JSON-RPC response: {0:?}")]
|
||||
ResponseParseError(grin_api::json_rpc::Error),
|
||||
}
|
||||
|
||||
/// Builds and verifies a 'Transaction' using the provided components.
|
||||
|
@ -29,7 +56,7 @@ pub fn assemble_tx(
|
|||
fee_base: u64,
|
||||
total_fee: u64,
|
||||
excesses: &Vec<SecretKey>,
|
||||
) -> Result<Transaction> {
|
||||
) -> Result<Transaction, WalletError> {
|
||||
let secp = Secp256k1::with_caps(ContextFlag::Commit);
|
||||
let txn_inputs = Inputs::from(inputs.as_slice());
|
||||
let mut txn_outputs = outputs.clone();
|
||||
|
@ -52,10 +79,8 @@ pub fn assemble_tx(
|
|||
let wallet_output = wallet.build_output(amount)?;
|
||||
txn_outputs.push(wallet_output.1);
|
||||
|
||||
let output_excess = wallet_output
|
||||
.0
|
||||
.secret_key(&secp)
|
||||
.map_err(|_| ErrorKind::CorruptedData)?;
|
||||
let output_excess = SecretKey::from_slice(&secp, &wallet_output.0.as_ref())
|
||||
.map_err(WalletError::OutputBlindError)?;
|
||||
txn_excesses.push(output_excess);
|
||||
}
|
||||
|
||||
|
@ -63,16 +88,20 @@ pub fn assemble_tx(
|
|||
let offset = secp::random_secret();
|
||||
|
||||
// calculate kernel excess
|
||||
let kern_excess = secp.blind_sum(txn_excesses, vec![offset.clone()])?;
|
||||
let kern_excess = secp
|
||||
.blind_sum(txn_excesses, vec![offset.clone()])
|
||||
.map_err(WalletError::KernelExcessError)?;
|
||||
|
||||
// build and verify kernel
|
||||
let mut kernel = TxKernel::with_features(KernelFeatures::Plain {
|
||||
fee: FeeFields::new(0, kernel_fee).unwrap(),
|
||||
fee: FeeFields::new(0, kernel_fee).map_err(WalletError::KernelFeeError)?,
|
||||
});
|
||||
let msg = kernel.msg_to_sign()?;
|
||||
kernel.excess = secp::commit(0, &kern_excess)?;
|
||||
kernel.excess_sig = secp::sign(&kern_excess, &msg)?;
|
||||
kernel.verify()?;
|
||||
let msg = kernel
|
||||
.msg_to_sign()
|
||||
.map_err(WalletError::KernelSigMessageError)?;
|
||||
kernel.excess = secp::commit(0, &kern_excess).map_err(WalletError::KernelExcessError)?;
|
||||
kernel.excess_sig = secp::sign(&kern_excess, &msg).map_err(WalletError::KernelSigError)?;
|
||||
kernel.verify().map_err(WalletError::KernelVerifyError)?;
|
||||
|
||||
// assemble the transaction
|
||||
let tx = Transaction::new(txn_inputs, &txn_outputs, &[kernel])
|
||||
|
@ -106,7 +135,7 @@ impl HttpWallet {
|
|||
wallet_owner_url: &SocketAddr,
|
||||
wallet_owner_secret: &Option<String>,
|
||||
wallet_pass: &ZeroingString,
|
||||
) -> Result<HttpWallet> {
|
||||
) -> Result<HttpWallet, WalletError> {
|
||||
println!("Opening wallet at {}", wallet_owner_url);
|
||||
let shared_key = HttpWallet::init_secure_api(&wallet_owner_url, &wallet_owner_secret)?;
|
||||
|
||||
|
@ -134,10 +163,10 @@ impl HttpWallet {
|
|||
fn init_secure_api(
|
||||
wallet_owner_url: &SocketAddr,
|
||||
wallet_owner_secret: &Option<String>,
|
||||
) -> Result<SecretKey> {
|
||||
) -> Result<SecretKey, WalletError> {
|
||||
let secp = Secp256k1::new();
|
||||
let ephemeral_sk = secp::random_secret();
|
||||
let ephemeral_pk = PublicKey::from_secret_key(&secp, &ephemeral_sk)?;
|
||||
let ephemeral_pk = PublicKey::from_secret_key(&secp, &ephemeral_sk).unwrap();
|
||||
let init_params = json!({
|
||||
"ecdh_pubkey": ephemeral_pk.serialize_vec(&secp, true).to_hex()
|
||||
});
|
||||
|
@ -151,10 +180,10 @@ impl HttpWallet {
|
|||
|
||||
let shared_key = {
|
||||
let mut shared_pubkey = response_pk.ecdh_pubkey.clone();
|
||||
shared_pubkey.mul_assign(&secp, &ephemeral_sk)?;
|
||||
shared_pubkey.mul_assign(&secp, &ephemeral_sk).unwrap();
|
||||
|
||||
let x_coord = shared_pubkey.serialize_vec(&secp, true);
|
||||
SecretKey::from_slice(&secp, &x_coord[1..])?
|
||||
SecretKey::from_slice(&secp, &x_coord[1..]).unwrap()
|
||||
};
|
||||
|
||||
Ok(shared_key)
|
||||
|
@ -166,7 +195,7 @@ impl HttpWallet {
|
|||
method: &str,
|
||||
params: &serde_json::Value,
|
||||
shared_key: &SecretKey,
|
||||
) -> Result<D> {
|
||||
) -> Result<D, WalletError> {
|
||||
let url = format!("http://{}{}", wallet_owner_url, ENDPOINT);
|
||||
let req = json!({
|
||||
"method": method,
|
||||
|
@ -174,15 +203,21 @@ impl HttpWallet {
|
|||
"id": JsonId::IntId(1),
|
||||
"jsonrpc": "2.0",
|
||||
});
|
||||
let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key)?;
|
||||
let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key)
|
||||
.map_err(WalletError::EncryptRequestError)?;
|
||||
let res = client::post::<EncryptedRequest, EncryptedResponse>(
|
||||
url.as_str(),
|
||||
wallet_owner_secret.clone(),
|
||||
&enc_req,
|
||||
)?;
|
||||
let decrypted = res.decrypt(&shared_key)?;
|
||||
let response: Response = serde_json::from_value(decrypted.clone())?;
|
||||
let parsed = serde_json::from_value(response.result.unwrap().get("Ok").unwrap().clone())?;
|
||||
)
|
||||
.map_err(WalletError::ApiCommError)?;
|
||||
let decrypted = res
|
||||
.decrypt(&shared_key)
|
||||
.map_err(WalletError::DecryptResponseError)?;
|
||||
let response: Response =
|
||||
serde_json::from_value(decrypted).map_err(WalletError::DecodeResponseError)?;
|
||||
let ok = response.result.unwrap().get("Ok").unwrap().clone();
|
||||
let parsed = serde_json::from_value(ok).map_err(WalletError::DecodeResponseError)?;
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
|
@ -191,12 +226,16 @@ impl HttpWallet {
|
|||
wallet_owner_secret: &Option<String>,
|
||||
method: &str,
|
||||
params: &serde_json::Value,
|
||||
) -> Result<D> {
|
||||
) -> Result<D, WalletError> {
|
||||
let url = format!("http://{}{}", wallet_owner_url, ENDPOINT);
|
||||
let req = build_request(method, params);
|
||||
let res =
|
||||
client::post::<Request, Response>(url.as_str(), wallet_owner_secret.clone(), &req)?;
|
||||
let parsed = res.clone().into_result()?;
|
||||
client::post::<Request, Response>(url.as_str(), wallet_owner_secret.clone(), &req)
|
||||
.map_err(WalletError::ApiCommError)?;
|
||||
let parsed = res
|
||||
.clone()
|
||||
.into_result()
|
||||
.map_err(WalletError::ResponseParseError)?;
|
||||
Ok(parsed)
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +252,7 @@ pub struct OutputWithBlind {
|
|||
|
||||
impl Wallet for HttpWallet {
|
||||
/// Builds an 'Output' for the wallet using the 'build_output' RPC API.
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)> {
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output), WalletError> {
|
||||
let req_json = json!({
|
||||
"token": self.token.keychain_mask.clone().unwrap().0,
|
||||
"features": "Plain",
|
||||
|
@ -232,8 +271,7 @@ impl Wallet for HttpWallet {
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod mock {
|
||||
use super::Wallet;
|
||||
use crate::error::Result;
|
||||
use super::{Wallet, WalletError};
|
||||
use crate::secp;
|
||||
|
||||
use grin_core::core::{Output, OutputFeatures};
|
||||
|
@ -246,10 +284,10 @@ pub mod mock {
|
|||
|
||||
impl Wallet for MockWallet {
|
||||
/// Builds an 'Output' for the wallet using the 'build_output' RPC API.
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)> {
|
||||
fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output), WalletError> {
|
||||
let secp = Secp256k1::new();
|
||||
let blind = secp::random_secret();
|
||||
let commit = secp::commit(amount, &blind)?;
|
||||
let commit = secp::commit(amount, &blind).unwrap();
|
||||
let proof = secp.bullet_proof(
|
||||
amount,
|
||||
blind.clone(),
|
||||
|
|
Loading…
Reference in a new issue