From a1e2e9d26d744372e708d87d5d4c316370d9fe18 Mon Sep 17 00:00:00 2001
From: Daksh <41485688+Daksh14@users.noreply.github.com>
Date: Thu, 5 Sep 2024 11:10:12 -0400
Subject: [PATCH 1/2] rusk-wallet: Upgrade to new `wallet-core`
- Change crate name to
- Acclimate to the new naming scheme of keys and types
- Zerorize secret keys
- Acclimate to new phoenix_balance method
---
rusk-wallet/Cargo.toml | 26 +-
rusk-wallet/src/bin/command.rs | 40 +-
rusk-wallet/src/bin/command/history.rs | 19 +-
rusk-wallet/src/bin/interactive.rs | 12 +-
rusk-wallet/src/bin/io/gql.rs | 17 +-
rusk-wallet/src/bin/io/prompt.rs | 12 +-
rusk-wallet/src/bin/main.rs | 14 +-
rusk-wallet/src/bin/settings.rs | 2 +-
rusk-wallet/src/cache.rs | 63 ++--
rusk-wallet/src/clients.rs | 480 +++++++++++-------------
rusk-wallet/src/clients/sync.rs | 96 ++---
rusk-wallet/src/currency.rs | 28 +-
rusk-wallet/src/dat.rs | 26 +-
rusk-wallet/src/error.rs | 44 +--
rusk-wallet/src/lib.rs | 22 +-
rusk-wallet/src/rusk.rs | 1 +
rusk-wallet/src/store.rs | 35 +-
rusk-wallet/src/wallet.rs | 486 ++++++++++++++-----------
rusk-wallet/src/wallet/address.rs | 38 +-
19 files changed, 739 insertions(+), 722 deletions(-)
diff --git a/rusk-wallet/Cargo.toml b/rusk-wallet/Cargo.toml
index 0bb2c4133..41a9226df 100644
--- a/rusk-wallet/Cargo.toml
+++ b/rusk-wallet/Cargo.toml
@@ -1,6 +1,6 @@
[package]
-name = "dusk-wallet"
-version = "0.22.1"
+name = "rusk-wallet"
+version = "0.1.0"
edition = "2021"
autobins = false
description = "A library providing functionalities to create wallets compatible with Dusk Network"
@@ -32,7 +32,6 @@ requestty = "0.5.0"
futures = "0.3"
base64 = "0.13"
crypto = "0.3"
-whoami = "1.2"
blake3 = "1.3"
sha2 = "0.10.7"
toml = "0.5"
@@ -44,24 +43,17 @@ aes = "0.7"
rocksdb = "0.22"
flume = "0.10.14"
reqwest = { version = "0.11", features = ["stream"] }
-
-dusk-wallet-core = "0.24.0-plonk.0.16-rc.2"
dusk-bytes = "0.1"
-dusk-pki = "0.13"
-rusk-abi = { version = "0.12.0-rc", default-features = false }
-phoenix-core = { version = "0.21", features = ["alloc"] }
-dusk-schnorr = { version = "0.14", default-features = false }
-dusk-poseidon = "0.31"
-dusk-plonk = "0.16"
-dusk-bls12_381-sign = { version = "0.5", default-features = false }
-ff = { version = "0.13", default-features = false }
-poseidon-merkle = "0.3"
+
+zeroize = { version = "1", default-features = false, features = ["derive"] }
+wallet-core = { path = "../wallet-core" }
+execution-core = { path = "../execution-core" }
tracing = "0.1"
tracing-subscriber = { version = "0.3.0", features = [
- "fmt",
- "env-filter",
- "json",
+ "fmt",
+ "env-filter",
+ "json",
] }
rkyv = { version = "=0.7.39", default-features = false }
diff --git a/rusk-wallet/src/bin/command.rs b/rusk-wallet/src/bin/command.rs
index a39066936..e75126097 100644
--- a/rusk-wallet/src/bin/command.rs
+++ b/rusk-wallet/src/bin/command.rs
@@ -7,17 +7,19 @@
mod history;
use clap::Subcommand;
-use dusk_plonk::prelude::BlsScalar;
-use rusk_abi::hash::Hasher;
use std::{fmt, path::PathBuf};
use crate::io::prompt;
use crate::settings::Settings;
use crate::{WalletFile, WalletPath};
-use dusk_wallet::gas::{Gas, DEFAULT_LIMIT, DEFAULT_PRICE};
-use dusk_wallet::{Address, Dusk, Lux, Wallet, EPOCH, MAX_ADDRESSES};
-use dusk_wallet_core::{BalanceInfo, StakeInfo};
+use execution_core::{stake::StakeData, BlsScalar};
+use rusk_wallet::{
+ currency::{Dusk, Lux},
+ gas::{Gas, DEFAULT_LIMIT, DEFAULT_PRICE},
+ Address, Error, Wallet, EPOCH, MAX_ADDRESSES,
+};
+use wallet_core::BalanceInfo;
pub use history::TransactionHistory;
@@ -230,7 +232,7 @@ impl Command {
let gas = Gas::new(gas_limit).with_price(gas_price);
let tx = wallet.transfer(sender, &rcvr, amt, gas).await?;
- Ok(RunResult::Tx(Hasher::digest(tx.to_hash_input_bytes())))
+ Ok(RunResult::Tx(tx.hash()))
}
Command::Stake {
addr,
@@ -246,14 +248,16 @@ impl Command {
let gas = Gas::new(gas_limit).with_price(gas_price);
let tx = wallet.stake(addr, amt, gas).await?;
- Ok(RunResult::Tx(Hasher::digest(tx.to_hash_input_bytes())))
+ Ok(RunResult::Tx(tx.hash()))
}
Command::StakeInfo { addr, reward } => {
let addr = match addr {
Some(addr) => wallet.claim_as_address(addr)?,
None => wallet.default_address(),
};
- let si = wallet.stake_info(addr).await?;
+ let si =
+ wallet.stake_info(addr).await?.ok_or(Error::NotStaked)?;
+
Ok(RunResult::StakeInfo(si, reward))
}
Command::Unstake {
@@ -270,7 +274,7 @@ impl Command {
let gas = Gas::new(gas_limit).with_price(gas_price);
let tx = wallet.unstake(addr, gas).await?;
- Ok(RunResult::Tx(Hasher::digest(tx.to_hash_input_bytes())))
+ Ok(RunResult::Tx(tx.hash()))
}
Command::Withdraw {
addr,
@@ -286,7 +290,7 @@ impl Command {
let gas = Gas::new(gas_limit).with_price(gas_price);
let tx = wallet.withdraw_reward(addr, gas).await?;
- Ok(RunResult::Tx(Hasher::digest(tx.to_hash_input_bytes())))
+ Ok(RunResult::Tx(tx.hash()))
}
Command::Export { addr, dir, name } => {
let addr = match addr {
@@ -329,7 +333,7 @@ impl Command {
pub enum RunResult {
Tx(BlsScalar),
Balance(BalanceInfo, bool),
- StakeInfo(StakeInfo, bool),
+ StakeInfo(StakeData, bool),
Address(Box
),
Addresses(Vec),
ExportedKeys(PathBuf, PathBuf),
@@ -366,13 +370,13 @@ impl fmt::Display for RunResult {
let hash = hex::encode(hash.to_bytes());
write!(f, "> Transaction sent: {hash}",)
}
- StakeInfo(si, _) => {
- let stake_str = match si.amount {
- Some((value, eligibility)) => format!(
+ StakeInfo(data, _) => {
+ let stake_str = match data.amount {
+ Some(amt) => format!(
"Current stake amount is: {} DUSK\n> Stake eligibility from block #{} (Epoch {})",
- Dusk::from(value),
- eligibility,
- eligibility / EPOCH
+ Dusk::from(amt.value),
+ amt.eligibility,
+ amt.eligibility / EPOCH
),
None => "No active stake found for this key".to_string(),
};
@@ -380,7 +384,7 @@ impl fmt::Display for RunResult {
f,
"> {}\n> Accumulated reward is: {} DUSK",
stake_str,
- Dusk::from(si.reward)
+ Dusk::from(data.reward)
)
}
ExportedKeys(pk, kp) => {
diff --git a/rusk-wallet/src/bin/command/history.rs b/rusk-wallet/src/bin/command/history.rs
index 2e8e573da..3ccc437c0 100644
--- a/rusk-wallet/src/bin/command/history.rs
+++ b/rusk-wallet/src/bin/command/history.rs
@@ -8,9 +8,9 @@ use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt::{self, Display};
-use dusk_wallet::DecodedNote;
-use dusk_wallet_core::Transaction;
-use rusk_abi::dusk;
+use rusk_wallet::DecodedNote;
+
+use execution_core::{dusk, from_dusk, transfer::Transaction};
use crate::io::{self, GraphQL};
use crate::settings::Settings;
@@ -35,17 +35,17 @@ impl TransactionHistory {
impl Display for TransactionHistory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let dusk = self.amount / dusk::dusk(1.0) as f64;
+ let dusk = self.amount / dusk(1.0) as f64;
let contract = match self.tx.call() {
None => "transfer",
- Some((_, method, _)) => method,
+ Some(call) => &call.fn_name,
};
let fee = match self.direction {
TransactionDirection::In => "".into(),
TransactionDirection::Out => {
let fee = self.fee;
- let fee = dusk::from_dusk(fee);
+ let fee = from_dusk(fee);
format!("{: >12.9}", fee)
}
};
@@ -95,8 +95,8 @@ pub(crate) async fn transaction_from_notes(
let note_hash = decoded_note.note.hash();
// Looking for the transaction which created the note
let note_creator = txs.iter().find(|(t, _, _)| {
- t.outputs().iter().any(|&n| n.hash().eq(¬e_hash))
- || t.nullifiers
+ t.outputs().iter().any(|n| n.hash().eq(¬e_hash))
+ || t.nullifiers()
.iter()
.any(|tx_null| nullifiers.iter().any(|(n, _)| n == tx_null))
});
@@ -114,13 +114,14 @@ pub(crate) async fn transaction_from_notes(
true => TransactionDirection::Out,
false => TransactionDirection::In,
};
+
match ret.iter_mut().find(|th| &th.id == tx_id) {
Some(tx) => tx.amount += note_amount,
None => ret.push(TransactionHistory {
direction,
height: decoded_note.block_height,
amount: note_amount - inputs_amount,
- fee: gas_spent * t.fee().gas_price,
+ fee: gas_spent * t.gas_price(),
tx: t.clone(),
id: tx_id.clone(),
}),
diff --git a/rusk-wallet/src/bin/interactive.rs b/rusk-wallet/src/bin/interactive.rs
index f6494ae3e..07f35cea3 100644
--- a/rusk-wallet/src/bin/interactive.rs
+++ b/rusk-wallet/src/bin/interactive.rs
@@ -5,10 +5,12 @@
// Copyright (c) DUSK NETWORK. All rights reserved.
use bip39::{Language, Mnemonic, MnemonicType};
-use dusk_wallet::dat::{DatFileVersion, LATEST_VERSION};
-use dusk_wallet::gas;
-use dusk_wallet::{Address, Dusk, Error, Wallet, WalletPath, MAX_ADDRESSES};
use requestty::Question;
+use rusk_wallet::{
+ currency::Dusk,
+ dat::{DatFileVersion, LATEST_VERSION},
+ gas, Address, Error, Wallet, WalletPath, MAX_ADDRESSES,
+};
use crate::command::DEFAULT_STAKE_GAS_LIMIT;
use crate::io;
@@ -68,7 +70,7 @@ pub(crate) async fn run_loop(
// get balance for this address
prompt::hide_cursor()?;
let balance = wallet.get_balance(&addr).await?;
- let spendable: Dusk = balance.spendable.into();
+ let spendable = balance.spendable.into();
let total: Dusk = balance.value.into();
prompt::hide_cursor()?;
@@ -159,7 +161,7 @@ fn menu_addr(wallet: &Wallet) -> anyhow::Result {
));
}
- if let Some(rx) = &wallet.sync_rx {
+ if let Some(rx) = &wallet.state()?.sync_rx {
if let Ok(status) = rx.try_recv() {
action_menu = action_menu
.separator()
diff --git a/rusk-wallet/src/bin/io/gql.rs b/rusk-wallet/src/bin/io/gql.rs
index d1fafc664..4b0c9d6c0 100644
--- a/rusk-wallet/src/bin/io/gql.rs
+++ b/rusk-wallet/src/bin/io/gql.rs
@@ -4,10 +4,10 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.
-use dusk_wallet_core::Transaction;
+use execution_core::transfer::Transaction;
use tokio::time::{sleep, Duration};
-use dusk_wallet::{Error, RuskHttpClient, RuskRequest};
+use rusk_wallet::{Error, RuskHttpClient, RuskRequest};
use serde::Deserialize;
/// GraphQL is a helper struct that aggregates all queries done
@@ -129,7 +129,7 @@ impl GraphQL {
pub enum GraphQLError {
/// Generic errors
#[error("Error fetching data from the node: {0}")]
- Generic(dusk_wallet::Error),
+ Generic(Error),
/// Failed to fetch transaction status
#[error("Failed to obtain transaction status")]
TxStatus,
@@ -137,8 +137,8 @@ pub enum GraphQLError {
BlockInfo,
}
-impl From for GraphQLError {
- fn from(e: dusk_wallet::Error) -> Self {
+impl From for GraphQLError {
+ fn from(e: Error) -> Self {
Self::Generic(e)
}
}
@@ -150,10 +150,7 @@ impl From for GraphQLError {
}
impl GraphQL {
- pub async fn query(
- &self,
- query: &str,
- ) -> Result, dusk_wallet::Error> {
+ pub async fn query(&self, query: &str) -> Result, Error> {
let request = RuskRequest::new("gql", query.as_bytes().to_vec());
self.client.call(2, "Chain", &request).await
}
@@ -177,7 +174,7 @@ async fn test() -> Result<(), Box> {
.await?;
let block_txs = gql.txs_for_block(90).await?;
block_txs.into_iter().for_each(|(t, chain_txid, _)| {
- let hash = rusk_abi::hash::Hasher::digest(t.to_hash_input_bytes());
+ let hash = t.hash();
let tx_id = hex::encode(hash.to_bytes());
assert_eq!(chain_txid, tx_id);
println!("txid: {tx_id}");
diff --git a/rusk-wallet/src/bin/io/prompt.rs b/rusk-wallet/src/bin/io/prompt.rs
index e4200d916..b358af147 100644
--- a/rusk-wallet/src/bin/io/prompt.rs
+++ b/rusk-wallet/src/bin/io/prompt.rs
@@ -15,13 +15,14 @@ use crossterm::{
use anyhow::Result;
use bip39::{ErrorKind, Language, Mnemonic};
-use dusk_wallet::{dat::DatFileVersion, Error};
use requestty::Question;
-use dusk_wallet::{Address, Dusk, Lux};
-
-use dusk_wallet::gas;
-use dusk_wallet::{MAX_CONVERTIBLE, MIN_CONVERTIBLE};
+use rusk_wallet::gas;
+use rusk_wallet::{
+ currency::{Dusk, Lux},
+ dat::DatFileVersion,
+ Address, Error, MAX_CONVERTIBLE, MIN_CONVERTIBLE,
+};
use sha2::{Digest, Sha256};
/// Request the user to authenticate with a password
@@ -244,6 +245,7 @@ pub(crate) fn request_token_amt(
.build();
let a = requestty::prompt_one(question)?;
+
Ok(a.as_float().expect("answer to be a float").into())
}
diff --git a/rusk-wallet/src/bin/main.rs b/rusk-wallet/src/bin/main.rs
index 7ddb9cada..050c4d874 100644
--- a/rusk-wallet/src/bin/main.rs
+++ b/rusk-wallet/src/bin/main.rs
@@ -12,7 +12,6 @@ mod menu;
mod settings;
pub(crate) use command::{Command, RunResult};
-use dusk_wallet::dat::LATEST_VERSION;
pub(crate) use menu::Menu;
use clap::Parser;
@@ -25,8 +24,11 @@ use bip39::{Language, Mnemonic, MnemonicType};
use crate::command::TransactionHistory;
use crate::settings::{LogFormat, Settings};
-use dusk_wallet::{dat, Error};
-use dusk_wallet::{Dusk, SecureWalletFile, Wallet, WalletPath};
+use rusk_wallet::{currency::Dusk, SecureWalletFile, Wallet, WalletPath};
+use rusk_wallet::{
+ dat::{self, LATEST_VERSION},
+ Error,
+};
use config::Config;
use io::{prompt, status};
@@ -83,7 +85,7 @@ where
// check for connection errors
match con {
- Err(Error::RocksDB(e)) => panic!{"Invalid cache {e}"},
+ Err(Error::RocksDB(e)) => panic!{"Please reset the cache! {e}"},
Err(e) => warn!("[OFFLINE MODE]: Unable to connect to Rusk, limited functionality available: {e}"),
_ => {}
}
@@ -121,7 +123,7 @@ async fn exec() -> anyhow::Result<()> {
// Finally complete the settings by setting the network
let settings = settings_builder
.network(cfg.network)
- .map_err(|_| dusk_wallet::Error::NetworkNotFound)?;
+ .map_err(|_| rusk_wallet::Error::NetworkNotFound)?;
// generate a subscriber with the desired log level
//
@@ -314,7 +316,7 @@ async fn exec() -> anyhow::Result<()> {
println!("{}", Dusk::from(info.reward));
} else {
let staked_amount = match info.amount {
- Some((staked, ..)) => staked,
+ Some(info) => info.value,
None => 0,
};
println!("{}", Dusk::from(staked_amount));
diff --git a/rusk-wallet/src/bin/settings.rs b/rusk-wallet/src/bin/settings.rs
index 99517c2e1..3284bb96d 100644
--- a/rusk-wallet/src/bin/settings.rs
+++ b/rusk-wallet/src/bin/settings.rs
@@ -7,7 +7,7 @@
use crate::config::Network;
use crate::io::WalletArgs;
-use dusk_wallet::Error;
+use rusk_wallet::Error;
use std::fmt;
use std::path::PathBuf;
diff --git a/rusk-wallet/src/cache.rs b/rusk-wallet/src/cache.rs
index acc7b3552..2dd4936d5 100644
--- a/rusk-wallet/src/cache.rs
+++ b/rusk-wallet/src/cache.rs
@@ -4,18 +4,15 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.
-use std::cmp::Ordering;
-use std::collections::BTreeSet;
use std::path::Path;
+use std::{cmp::Ordering, collections::BTreeSet};
use dusk_bytes::{DeserializableSlice, Serializable};
-use dusk_pki::PublicSpendKey;
-use dusk_plonk::prelude::BlsScalar;
-use dusk_wallet_core::Store;
-use phoenix_core::Note;
use rocksdb::{DBWithThreadMode, MultiThreaded, Options};
-use crate::{error::Error, store::LocalStore, MAX_ADDRESSES};
+use super::*;
+
+use crate::error::Error;
type DB = DBWithThreadMode;
@@ -30,21 +27,9 @@ impl Cache {
/// Returns a new cache instance.
pub(crate) fn new>(
path: T,
- store: &LocalStore,
+ cfs: Vec,
status: fn(&str),
) -> Result {
- let cfs: Vec<_> = (0..MAX_ADDRESSES)
- .flat_map(|i| {
- let ssk =
- store.retrieve_ssk(i as u64).expect("ssk to be available");
- let psk = ssk.public_spend_key();
-
- let live = format!("{:?}", psk);
- let spent = format!("spent_{:?}", psk);
- [live, spent]
- })
- .collect();
-
status("Opening notes database");
let mut opts = Options::default();
@@ -59,16 +44,16 @@ impl Cache {
Ok(Self { db })
}
- // We store a column family named by hex representation of the psk.
+ // We store a column family named by hex representation of the pk.
// We store the nullifier of the note as key and the value is the bytes
// representation of the tuple (NoteHeight, Note)
pub(crate) fn insert(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
height: u64,
note_data: (Note, BlsScalar),
) -> Result<(), Error> {
- let cf_name = format!("{:?}", psk);
+ let cf_name = format!("{:?}", pk);
let cf = self
.db
@@ -85,16 +70,16 @@ impl Cache {
Ok(())
}
- // We store a column family named by hex representation of the psk.
+ // We store a column family named by hex representation of the pk.
// We store the nullifier of the note as key and the value is the bytes
// representation of the tuple (NoteHeight, Note)
pub(crate) fn insert_spent(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
height: u64,
note_data: (Note, BlsScalar),
) -> Result<(), Error> {
- let cf_name = format!("spent_{:?}", psk);
+ let cf_name = format!("spent_{:?}", pk);
let cf = self
.db
@@ -113,14 +98,14 @@ impl Cache {
pub(crate) fn spend_notes(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
nullifiers: &[BlsScalar],
) -> Result<(), Error> {
if nullifiers.is_empty() {
return Ok(());
}
- let cf_name = format!("{:?}", psk);
- let spent_cf_name = format!("spent_{:?}", psk);
+ let cf_name = format!("{:?}", pk);
+ let spent_cf_name = format!("spent_{:?}", pk);
let cf = self
.db
@@ -160,12 +145,12 @@ impl Cache {
}))
}
- /// Returns an iterator over all unspent notes nullifier for the given PSK.
+ /// Returns an iterator over all unspent notes nullifier for the given pk.
pub(crate) fn unspent_notes_id(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
) -> Result, Error> {
- let cf_name = format!("{:?}", psk);
+ let cf_name = format!("{:?}", pk);
let mut notes = vec![];
if let Some(cf) = self.db.cf_handle(&cf_name) {
@@ -183,13 +168,13 @@ impl Cache {
Ok(notes)
}
- /// Returns an iterator over all unspent notes inserted for the given PSK,
+ /// Returns an iterator over all unspent notes inserted for the given pk,
/// in order of note position.
pub(crate) fn notes(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
) -> Result, Error> {
- let cf_name = format!("{:?}", psk);
+ let cf_name = format!("{:?}", pk);
let mut notes = BTreeSet::::new();
if let Some(cf) = self.db.cf_handle(&cf_name) {
@@ -208,13 +193,13 @@ impl Cache {
Ok(notes)
}
- /// Returns an iterator over all notes inserted for the given PSK, in order
+ /// Returns an iterator over all notes inserted for the given pk, in order
/// of block height.
pub(crate) fn spent_notes(
&self,
- psk: &PublicSpendKey,
+ pk: &PhoenixPublicKey,
) -> Result, Error> {
- let cf_name = format!("spent_{:?}", psk);
+ let cf_name = format!("spent_{:?}", pk);
let mut notes = vec![];
if let Some(cf) = self.db.cf_handle(&cf_name) {
@@ -249,7 +234,7 @@ impl PartialOrd for NoteData {
}
impl Ord for NoteData {
- fn cmp(&self, other: &Self) -> Ordering {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.note.pos().cmp(other.note.pos())
}
}
diff --git a/rusk-wallet/src/clients.rs b/rusk-wallet/src/clients.rs
index 35a5de5af..4c1a7ff9f 100644
--- a/rusk-wallet/src/clients.rs
+++ b/rusk-wallet/src/clients.rs
@@ -6,43 +6,34 @@
mod sync;
-use dusk_bls12_381_sign::PublicKey;
-use dusk_bytes::{DeserializableSlice, Serializable, Write};
-use dusk_pki::ViewKey;
-use dusk_plonk::prelude::*;
-use dusk_plonk::proof_system::Proof;
-use dusk_schnorr::Signature;
-use dusk_wallet_core::{
- EnrichedNote, ProverClient, StakeInfo, StateClient, Transaction,
- UnprovenTransaction, POSEIDON_TREE_DEPTH,
+use dusk_bytes::Serializable;
+use execution_core::{
+ transfer::{phoenix::Prove, Transaction},
+ Error as ExecutionCoreError,
};
-use flume::Sender;
-use phoenix_core::transaction::StakeData;
-use phoenix_core::{Crossover, Fee, Note};
-use poseidon_merkle::Opening as PoseidonOpening;
+use flume::Receiver;
use tokio::time::{sleep, Duration};
+use wallet_core::{
+ input::try_input_notes,
+ keys::{derive_phoenix_pk, derive_phoenix_sk, derive_phoenix_vk},
+};
+use zeroize::Zeroize;
-use std::path::Path;
-use std::sync::{Arc, Mutex};
+use std::{
+ path::Path,
+ sync::{Arc, Mutex},
+};
use self::sync::sync_db;
-use super::block::Block;
-use super::cache::Cache;
-
-use crate::rusk::{RuskHttpClient, RuskRequest};
-use crate::store::LocalStore;
-use crate::Error;
-
-const STCT_INPUT_SIZE: usize = Fee::SIZE
- + Crossover::SIZE
- + u64::SIZE
- + JubJubScalar::SIZE
- + BlsScalar::SIZE
- + Signature::SIZE;
+use super::{block::Block, cache::Cache, *};
-const WFCT_INPUT_SIZE: usize =
- JubJubAffine::SIZE + u64::SIZE + JubJubScalar::SIZE;
+use crate::{
+ cache::NoteData,
+ rusk::{RuskHttpClient, RuskRequest},
+ store::LocalStore,
+ Error, MAX_ADDRESSES,
+};
const TRANSFER_CONTRACT: &str =
"0100000000000000000000000000000000000000000000000000000000000000";
@@ -53,191 +44,84 @@ const STAKE_CONTRACT: &str =
// Sync every 3 seconds for now
const SYNC_INTERVAL_SECONDS: u64 = 3;
-/// Implementation of the ProverClient trait from wallet-core
-pub struct Prover {
- state: RuskHttpClient,
- prover: RuskHttpClient,
- status: fn(status: &str),
-}
-
-impl Prover {
- pub fn new(state: RuskHttpClient, prover: RuskHttpClient) -> Self {
- Prover {
- state,
- prover,
- status: |_| {},
- }
- }
+/// A prover struct that has the `Prove` trait from executio-core implemented.
+/// It currently uses a hardcoded prover which delegates the proving to the
+/// `prove_execute`
+pub struct Prover;
- /// Sets the callback method to send status updates
- pub fn set_status_callback(&mut self, status: fn(&str)) {
- self.status = status;
- }
-
- pub async fn check_connection(&self) -> Result<(), reqwest::Error> {
- self.state.check_connection().await?;
- self.prover.check_connection().await
+impl Prove for Prover {
+ fn prove(
+ tx_circuit_vec_bytes: &[u8],
+ ) -> Result, ExecutionCoreError> {
+ Ok(tx_circuit_vec_bytes.to_vec())
}
}
-impl ProverClient for Prover {
- /// Error returned by the prover client.
- type Error = Error;
-
- /// Requests that a node prove the given transaction and later propagates it
- fn compute_proof_and_propagate(
- &self,
- utx: &UnprovenTransaction,
- ) -> Result {
- self.status("Proving tx, please wait...");
- let utx_bytes = utx.to_var_bytes();
- let prove_req = RuskRequest::new("prove_execute", utx_bytes);
- let proof_bytes = self.prover.call(2, "rusk", &prove_req).wait()?;
- self.status("Proof success!");
- let proof = Proof::from_slice(&proof_bytes).map_err(Error::Bytes)?;
- let tx = utx.clone().prove(proof);
- let tx_bytes = tx.to_var_bytes();
-
- self.status("Attempt to preverify tx...");
- let preverify_req = RuskRequest::new("preverify", tx_bytes.clone());
- let _ = self.state.call(2, "rusk", &preverify_req).wait()?;
- self.status("Preverify success!");
-
- self.status("Propagating tx...");
- let propagate_req = RuskRequest::new("propagate_tx", tx_bytes);
- let _ = self.state.call(2, "Chain", &propagate_req).wait()?;
- self.status("Transaction propagated!");
-
- Ok(tx)
- }
-
- /// Requests an STCT proof.
- fn request_stct_proof(
- &self,
- fee: &Fee,
- crossover: &Crossover,
- value: u64,
- blinder: JubJubScalar,
- address: BlsScalar,
- signature: Signature,
- ) -> Result {
- let mut buf = [0; STCT_INPUT_SIZE];
- let mut writer = &mut buf[..];
- writer.write(&fee.to_bytes())?;
- writer.write(&crossover.to_bytes())?;
- writer.write(&value.to_bytes())?;
- writer.write(&blinder.to_bytes())?;
- writer.write(&address.to_bytes())?;
- writer.write(&signature.to_bytes())?;
-
- self.status("Requesting stct proof...");
-
- let prove_req = RuskRequest::new("prove_stct", buf.to_vec());
- let res = self.prover.call(2, "rusk", &prove_req).wait()?;
-
- self.status("Stct proof success!");
-
- let mut proof_bytes = [0u8; Proof::SIZE];
- proof_bytes.copy_from_slice(&res);
-
- let proof = Proof::from_bytes(&proof_bytes)?;
- Ok(proof)
- }
-
- /// Request a WFCT proof.
- fn request_wfct_proof(
- &self,
- commitment: JubJubAffine,
- value: u64,
- blinder: JubJubScalar,
- ) -> Result {
- let mut buf = [0; WFCT_INPUT_SIZE];
- let mut writer = &mut buf[..];
- writer.write(&commitment.to_bytes())?;
- writer.write(&value.to_bytes())?;
- writer.write(&blinder.to_bytes())?;
-
- self.status("Requesting wfct proof...");
- let prove_req = RuskRequest::new("prove_wfct", buf.to_vec());
- let res = self.prover.call(2, "rusk", &prove_req).wait()?;
- self.status("Wfct proof success!");
-
- let mut proof_bytes = [0u8; Proof::SIZE];
- proof_bytes.copy_from_slice(&res);
-
- let proof = Proof::from_bytes(&proof_bytes)?;
- Ok(proof)
- }
-}
-
-impl Prover {
- fn status(&self, text: &str) {
- (self.status)(text)
- }
-}
-
-/// Implementation of the StateClient trait from wallet-core
-/// inner is an option because we don't want to open the db twice and lock it
-/// We construct StateStore twice
-pub struct StateStore {
- inner: Mutex,
+/// The state struct is responsible for managing the state of the wallet
+pub struct State {
+ cache: Mutex>,
status: fn(&str),
- pub(crate) store: LocalStore,
-}
-
-struct InnerState {
client: RuskHttpClient,
- cache: Arc,
+ prover: RuskHttpClient,
+ store: LocalStore,
+ pub sync_rx: Option>,
}
-impl StateStore {
+impl State {
/// Creates a new state instance. Should only be called once.
pub(crate) fn new(
- client: RuskHttpClient,
data_dir: &Path,
- store: LocalStore,
status: fn(&str),
+ client: RuskHttpClient,
+ prover: RuskHttpClient,
+ store: LocalStore,
) -> Result {
- let cache = Arc::new(Cache::new(data_dir, &store, status)?);
- let inner = Mutex::new(InnerState { client, cache });
+ let cfs = (0..MAX_ADDRESSES)
+ .flat_map(|i| {
+ let pk: PhoenixPublicKey =
+ derive_phoenix_pk(store.get_seed(), i as u8);
+
+ [format!("{:?}", pk), format!("spent_{:?}", pk)]
+ })
+ .collect();
+
+ let cache = Mutex::new(Arc::new(Cache::new(data_dir, cfs, status)?));
Ok(Self {
- inner,
- status,
+ cache,
+ sync_rx: None,
store,
+ prover,
+ status,
+ client,
})
}
- pub async fn check_connection(&self) -> Result<(), reqwest::Error> {
- let client = { self.inner.lock().unwrap().client.clone() };
+ pub(crate) fn cache(&self) -> Arc {
+ let state = self.cache.lock().unwrap();
- client.check_connection().await
+ Arc::clone(&state)
}
- pub async fn register_sync(
- &self,
- sync_tx: Sender,
- ) -> Result<(), Error> {
- let state = self.inner.lock().unwrap();
+ pub async fn register_sync(&mut self) -> Result<(), Error> {
+ let (sync_tx, sync_rx) = flume::unbounded::();
+
+ self.sync_rx = Some(sync_rx);
+
+ let cache = self.cache();
let status = self.status;
+ let client = self.client.clone();
let store = self.store.clone();
- let client = state.client.clone();
- let cache = Arc::clone(&state.cache);
- let sender = Arc::new(sync_tx);
status("Starting Sync..");
tokio::spawn(async move {
loop {
- let status = |_: &_| {};
- let sender = Arc::clone(&sender);
- let _ = sender.send("Syncing..".to_string());
-
- let sync_status =
- sync_db(&client, &store, &cache, status).await;
- let _ = match sync_status {
- Ok(_) => sender.send("Syncing Complete".to_string()),
- Err(e) => sender.send(format!("Error during sync:.. {e}")),
+ let _ = sync_tx.send("Syncing..".to_string());
+
+ let _ = match sync_db(&client, &cache, &store, status).await {
+ Ok(_) => sync_tx.send("Syncing Complete".to_string()),
+ Err(e) => sync_tx.send(format!("Error during sync:.. {e}")),
};
sleep(Duration::from_secs(SYNC_INTERVAL_SECONDS)).await;
@@ -248,133 +132,179 @@ impl StateStore {
}
pub async fn sync(&self) -> Result<(), Error> {
- let store = self.store.clone();
+ sync_db(&self.client, &self.cache(), &self.store, self.status).await
+ }
+
+ /// Requests that a node prove the given transaction and later propagates it
+ /// Skips writing the proof for non phoenix transactions
+ pub fn prove_and_propagate(
+ &self,
+ utx: Transaction,
+ ) -> Result {
let status = self.status;
- let (cache, client) = {
- let state = self.inner.lock().unwrap();
+ let prover = &self.prover;
+ let mut utx = utx;
- let cache = state.cache.clone();
- let client = state.client.clone();
- (cache, client)
- };
+ if let Transaction::Phoenix(tx) = &mut utx {
+ let status = self.status;
+ let proof = tx.proof();
- sync_db(&client, &store, cache.as_ref(), status).await
- }
+ status("Attempt to prove tx...");
- pub(crate) fn cache(&self) -> Arc {
- let state = self.inner.lock().unwrap();
- Arc::clone(&state.cache)
- }
-}
+ let prove_req = RuskRequest::new("prove_execute", proof.to_vec());
+
+ let proof =
+ prover.call(2, "rusk", &prove_req).wait().map_err(|e| {
+ ExecutionCoreError::PhoenixCircuit(e.to_string())
+ })?;
+
+ tx.set_proof(proof);
+
+ status("Proving sucesss!");
+ }
-/// Types that are clients of the state API.
-impl StateClient for StateStore {
- /// Error returned by the node client.
- type Error = Error;
+ let tx_bytes = utx.to_var_bytes();
+
+ status("Attempt to preverify tx...");
+ let preverify_req = RuskRequest::new("preverify", tx_bytes.clone());
+ let _ = self.client.call(2, "rusk", &preverify_req).wait()?;
+ status("Preverify success!");
+
+ status("Propagating tx...");
+ let propagate_req = RuskRequest::new("propagate_tx", tx_bytes);
+ let _ = self.client.call(2, "Chain", &propagate_req).wait()?;
+ status("Transaction propagated!");
+
+ Ok(utx)
+ }
/// Find notes for a view key, starting from the given block height.
- fn fetch_notes(
+ pub(crate) fn inputs(
&self,
- vk: &ViewKey,
- ) -> Result, Self::Error> {
- let psk = vk.public_spend_key();
- let state = self.inner.lock().unwrap();
-
- Ok(state
- .cache
- .notes(&psk)?
+ index: u8,
+ target: u64,
+ ) -> Result, Error> {
+ let vk = derive_phoenix_vk(self.store().get_seed(), index);
+ let mut sk = derive_phoenix_sk(self.store().get_seed(), index);
+ let pk = derive_phoenix_pk(self.store().get_seed(), index);
+
+ let inputs: Result, Error> = self
+ .cache()
+ .notes(&pk)?
.into_iter()
- .map(|data| (data.note, data.height))
- .collect())
- }
+ .map(|data| {
+ let note = data.note;
+ let nullifiers = note.gen_nullifier(&sk);
+ let value = note.value(Some(&vk)).unwrap();
- /// Fetch the current anchor of the state.
- fn fetch_anchor(&self) -> Result {
- let state = self.inner.lock().unwrap();
+ Ok((note, value, nullifiers))
+ })
+ .collect();
- self.status("Fetching anchor...");
+ let inputs = try_input_notes(inputs?, target)
+ .into_iter()
+ .map(|(note, scalar)| {
+ let opening = self.fetch_opening(¬e)?;
- let anchor = state
- .client
- .contract_query::<(), 0>(TRANSFER_CONTRACT, "root", &())
- .wait()?;
- self.status("Anchor received!");
- let anchor = rkyv::from_bytes(&anchor).map_err(|_| Error::Rkyv)?;
- Ok(anchor)
- }
+ Ok((note, opening, scalar))
+ })
+ .collect();
- /// Asks the node to return the nullifiers that already exist from the given
- /// nullifiers.
- fn fetch_existing_nullifiers(
- &self,
- _nullifiers: &[BlsScalar],
- ) -> Result, Self::Error> {
- Ok(vec![])
+ sk.zeroize();
+
+ inputs
}
- /// Queries the node to find the opening for a specific note.
- fn fetch_opening(
+ pub(crate) fn fetch_notes(
&self,
- note: &Note,
- ) -> Result, Self::Error> {
- let state = self.inner.lock().unwrap();
+ pk: &PhoenixPublicKey,
+ ) -> Result, Error> {
+ self.cache()
+ .notes(pk)?
+ .into_iter()
+ .map(|data| {
+ Ok(NoteData {
+ note: data.note,
+ height: data.height,
+ })
+ })
+ .collect()
+ }
- self.status("Fetching opening notes...");
+ /// Fetch the current root of the state.
+ pub(crate) fn fetch_root(&self) -> Result {
+ let status = self.status;
+ status("Fetching root...");
- let data = state
+ let root = self
.client
- .contract_query::<_, 1024>(TRANSFER_CONTRACT, "opening", note.pos())
+ .contract_query::<(), 0>(TRANSFER_CONTRACT, "root", &())
.wait()?;
-
- self.status("Opening notes received!");
-
- let branch = rkyv::from_bytes(&data).map_err(|_| Error::Rkyv)?;
- Ok(branch)
+ status("root received!");
+ let root = rkyv::from_bytes(&root).map_err(|_| Error::Rkyv)?;
+ Ok(root)
}
/// Queries the node for the amount staked by a key.
- fn fetch_stake(&self, pk: &PublicKey) -> Result {
- let state = self.inner.lock().unwrap();
-
- self.status("Fetching stake...");
+ pub(crate) fn fetch_stake(
+ &self,
+ pk: &AccountPublicKey,
+ ) -> Result