|
|
@@ -1,5 +1,6 @@
|
|
|
use async_executor::Executor;
|
|
|
use async_std::sync::{Arc, Mutex};
|
|
|
+
|
|
|
use bellman::groth16;
|
|
|
use bls12_381::Bls12;
|
|
|
use log::{debug, info, warn};
|
|
|
@@ -8,6 +9,7 @@ use url::Url;
|
|
|
use crate::{
|
|
|
blockchain::{rocks::columns, Rocks, RocksColumn, Slab},
|
|
|
crypto::{
|
|
|
+ coin::Coin,
|
|
|
merkle::{CommitmentTree, IncrementalWitness},
|
|
|
merkle_node::MerkleNode,
|
|
|
note::{EncryptedNote, Note},
|
|
|
@@ -35,6 +37,7 @@ pub enum ClientFailed {
|
|
|
WalletInitialized,
|
|
|
KeyExists,
|
|
|
ClientError(String),
|
|
|
+ VerifyError(String),
|
|
|
}
|
|
|
|
|
|
pub struct Client {
|
|
|
@@ -93,7 +96,6 @@ impl Client {
|
|
|
amount: u64,
|
|
|
state: Arc<Mutex<State>>,
|
|
|
) -> ClientResult<()> {
|
|
|
-
|
|
|
debug!(target: "CLIENT", "Start transfer {}", amount);
|
|
|
|
|
|
let token_id_exists = self.wallet.token_id_exists(&token_id)?;
|
|
|
@@ -123,15 +125,12 @@ impl Client {
|
|
|
return Err(ClientFailed::InvalidAmount(amount as u64));
|
|
|
}
|
|
|
|
|
|
- let (slab, tx) = self
|
|
|
- .build_slab_from_tx(pub_key, amount, token_id, clear_input)
|
|
|
+ let coins = self
|
|
|
+ .build_slab_from_tx(pub_key, amount, token_id, clear_input, state)
|
|
|
.await?;
|
|
|
|
|
|
- // check if it's valid before send it to gateway
|
|
|
- if let Err(err) = self.update_state(tx, state).await {
|
|
|
- return Err(ClientFailed::from(err));
|
|
|
- } else {
|
|
|
- self.gateway.put_slab(slab).await?;
|
|
|
+ for coin in coins.iter() {
|
|
|
+ self.wallet.confirm_spend_coin(coin)?;
|
|
|
}
|
|
|
|
|
|
debug!(target: "CLIENT", "End send {}", amount);
|
|
|
@@ -140,17 +139,19 @@ impl Client {
|
|
|
}
|
|
|
|
|
|
async fn build_slab_from_tx(
|
|
|
- &self,
|
|
|
+ &mut self,
|
|
|
pub_key: jubjub::SubgroupPoint,
|
|
|
value: u64,
|
|
|
token_id: jubjub::Fr,
|
|
|
clear_input: bool,
|
|
|
- ) -> Result<(Slab, tx::Transaction)> {
|
|
|
+ state: Arc<Mutex<State>>,
|
|
|
+ ) -> ClientResult<Vec<Coin>> {
|
|
|
debug!(target: "CLIENT", "Start build slab from tx");
|
|
|
|
|
|
let mut clear_inputs: Vec<tx::TransactionBuilderClearInputInfo> = vec![];
|
|
|
let mut inputs: Vec<tx::TransactionBuilderInputInfo> = vec![];
|
|
|
let mut outputs: Vec<tx::TransactionBuilderOutputInfo> = vec![];
|
|
|
+ let mut coins: Vec<Coin> = vec![];
|
|
|
|
|
|
if clear_input {
|
|
|
let signature_secret = self.main_keypair.private;
|
|
|
@@ -161,7 +162,45 @@ impl Client {
|
|
|
};
|
|
|
clear_inputs.push(input);
|
|
|
} else {
|
|
|
- inputs = self.build_inputs(value, token_id, &mut outputs).await?;
|
|
|
+ debug!(target: "CLIENT", "Start build inputs");
|
|
|
+
|
|
|
+ let mut inputs_value: u64 = 0;
|
|
|
+
|
|
|
+ let own_coins = self.wallet.get_own_coins()?;
|
|
|
+
|
|
|
+ for own_coin in own_coins.iter() {
|
|
|
+ if inputs_value >= value {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ let witness = &own_coin.witness;
|
|
|
+ let merkle_path = witness.path().unwrap();
|
|
|
+ inputs_value += own_coin.note.value;
|
|
|
+
|
|
|
+ let input = tx::TransactionBuilderInputInfo {
|
|
|
+ merkle_path,
|
|
|
+ secret: own_coin.secret,
|
|
|
+ note: own_coin.note.clone(),
|
|
|
+ };
|
|
|
+
|
|
|
+ inputs.push(input);
|
|
|
+ coins.push(own_coin.coin.clone());
|
|
|
+ }
|
|
|
+
|
|
|
+ if inputs_value < value {
|
|
|
+ return Err(ClientFailed::NotEnoughValue(inputs_value).into());
|
|
|
+ }
|
|
|
+
|
|
|
+ if inputs_value > value {
|
|
|
+ let return_value: u64 = inputs_value - value;
|
|
|
+
|
|
|
+ outputs.push(tx::TransactionBuilderOutputInfo {
|
|
|
+ value: return_value,
|
|
|
+ token_id,
|
|
|
+ public: self.main_keypair.public,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ debug!(target: "CLIENT", "End build inputs");
|
|
|
}
|
|
|
|
|
|
outputs.push(tx::TransactionBuilderOutputInfo {
|
|
|
@@ -188,56 +227,13 @@ impl Client {
|
|
|
|
|
|
debug!(target: "CLIENT", "End build slab from tx");
|
|
|
|
|
|
- Ok((slab, tx))
|
|
|
- }
|
|
|
-
|
|
|
- async fn build_inputs(
|
|
|
- &self,
|
|
|
- amount: u64,
|
|
|
- token_id: jubjub::Fr,
|
|
|
- outputs: &mut Vec<tx::TransactionBuilderOutputInfo>,
|
|
|
- ) -> Result<Vec<tx::TransactionBuilderInputInfo>> {
|
|
|
- debug!(target: "CLIENT", "Start build inputs");
|
|
|
-
|
|
|
- let mut inputs: Vec<tx::TransactionBuilderInputInfo> = vec![];
|
|
|
- let mut inputs_value: u64 = 0;
|
|
|
-
|
|
|
- let own_coins = self.wallet.get_own_coins()?;
|
|
|
-
|
|
|
- for own_coin in own_coins.iter() {
|
|
|
- if inputs_value >= amount {
|
|
|
- break;
|
|
|
- }
|
|
|
- self.wallet.confirm_spend_coin(&own_coin.coin)?;
|
|
|
- let witness = &own_coin.witness;
|
|
|
- let merkle_path = witness.path().unwrap();
|
|
|
- inputs_value += own_coin.note.value;
|
|
|
- let input = tx::TransactionBuilderInputInfo {
|
|
|
- merkle_path,
|
|
|
- secret: own_coin.secret,
|
|
|
- note: own_coin.note.clone(),
|
|
|
- };
|
|
|
-
|
|
|
- inputs.push(input);
|
|
|
- }
|
|
|
-
|
|
|
- if inputs_value < amount {
|
|
|
- return Err(ClientFailed::NotEnoughValue(inputs_value).into());
|
|
|
- }
|
|
|
-
|
|
|
- if inputs_value > amount {
|
|
|
- let return_value: u64 = inputs_value - amount;
|
|
|
-
|
|
|
- outputs.push(tx::TransactionBuilderOutputInfo {
|
|
|
- value: return_value,
|
|
|
- token_id,
|
|
|
- public: self.main_keypair.public,
|
|
|
- });
|
|
|
- }
|
|
|
+ // check if it's valid before send it to gateway
|
|
|
+ let state = state.lock().await;
|
|
|
+ state_transition(&state, tx)?;
|
|
|
|
|
|
- debug!(target: "CLIENT", "End build inputs");
|
|
|
+ self.gateway.put_slab(slab).await?;
|
|
|
|
|
|
- Ok(inputs)
|
|
|
+ Ok(coins)
|
|
|
}
|
|
|
|
|
|
pub async fn connect_to_subscriber_from_cashier(
|
|
|
@@ -261,38 +257,21 @@ impl Client {
|
|
|
|
|
|
debug!(target: "CLIENT", "Received new slab");
|
|
|
|
|
|
- debug!(target: "CLIENT", "Starting build tx from slab");
|
|
|
- let tx = tx::Transaction::decode(&slab.get_payload()[..]);
|
|
|
-
|
|
|
- if let Err(e) = tx {
|
|
|
- warn!("TX: {}", e.to_string());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- let mut state = state.lock().await;
|
|
|
-
|
|
|
- let update = state_transition(&state, tx?);
|
|
|
-
|
|
|
- if let Err(e) = update {
|
|
|
- warn!("state transition: {}", e.to_string());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
let mut secret_keys: Vec<jubjub::Fr> = vec![secret_key];
|
|
|
let mut withdraw_keys = cashier_wallet.get_withdraw_private_keys()?;
|
|
|
secret_keys.append(&mut withdraw_keys);
|
|
|
|
|
|
- let state_apply = state
|
|
|
- .apply(
|
|
|
- update?,
|
|
|
- secret_keys.clone(),
|
|
|
- Some(notify.clone()),
|
|
|
- wallet.clone(),
|
|
|
- )
|
|
|
- .await;
|
|
|
-
|
|
|
- if let Err(e) = state_apply {
|
|
|
- warn!("apply state: {}", e.to_string());
|
|
|
+ let update_state = Self::update_state(
|
|
|
+ secret_keys,
|
|
|
+ &slab,
|
|
|
+ state.clone(),
|
|
|
+ wallet.clone(),
|
|
|
+ Some(notify.clone()),
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+
|
|
|
+ if let Err(e) = update_state {
|
|
|
+ warn!("Update state: {}", e.to_string());
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
@@ -322,32 +301,17 @@ impl Client {
|
|
|
|
|
|
debug!(target: "CLIENT", "Received new slab");
|
|
|
|
|
|
- debug!(target: "CLIENT", "Starting build tx from slab");
|
|
|
-
|
|
|
- let tx = tx::Transaction::decode(&slab.get_payload()[..]);
|
|
|
-
|
|
|
- if let Err(e) = tx {
|
|
|
- warn!("TX Decode: {}", e.to_string());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- let mut state = state.lock().await;
|
|
|
-
|
|
|
- let update = state_transition(&state, tx?);
|
|
|
-
|
|
|
- if let Err(e) = update {
|
|
|
- warn!("state transition: {}", e.to_string());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- let secret_keys: Vec<jubjub::Fr> = vec![secret_key];
|
|
|
-
|
|
|
- let state_apply = state
|
|
|
- .apply(update?, secret_keys.clone(), None, wallet.clone())
|
|
|
- .await;
|
|
|
-
|
|
|
- if let Err(e) = state_apply {
|
|
|
- warn!("apply state: {}", e.to_string());
|
|
|
+ let update_state = Self::update_state(
|
|
|
+ vec![secret_key],
|
|
|
+ &slab,
|
|
|
+ state.clone(),
|
|
|
+ wallet.clone(),
|
|
|
+ None,
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+
|
|
|
+ if let Err(e) = update_state {
|
|
|
+ warn!("Update state: {}", e.to_string());
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
@@ -358,15 +322,23 @@ impl Client {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- async fn update_state(&self, tx: tx::Transaction, state: Arc<Mutex<State>>) -> Result<()> {
|
|
|
+ async fn update_state(
|
|
|
+ secret_keys: Vec<jubjub::Fr>,
|
|
|
+ slab: &Slab,
|
|
|
+ state: Arc<Mutex<State>>,
|
|
|
+ wallet: WalletPtr,
|
|
|
+ notify: Option<async_channel::Sender<(jubjub::SubgroupPoint, u64)>>,
|
|
|
+ ) -> Result<()> {
|
|
|
+ debug!(target: "CLIENT", "Build tx from slab and update the state");
|
|
|
+
|
|
|
+ let tx = tx::Transaction::decode(&slab.get_payload()[..])?;
|
|
|
+
|
|
|
let mut state = state.lock().await;
|
|
|
|
|
|
let update = state_transition(&state, tx)?;
|
|
|
|
|
|
- let secret_keys: Vec<jubjub::Fr> = vec![self.main_keypair.private];
|
|
|
-
|
|
|
state
|
|
|
- .apply(update, secret_keys.clone(), None, self.wallet.clone())
|
|
|
+ .apply(update, secret_keys.clone(), notify, wallet)
|
|
|
.await?;
|
|
|
|
|
|
Ok(())
|
|
|
@@ -376,19 +348,27 @@ impl Client {
|
|
|
self.wallet.init_db().await
|
|
|
}
|
|
|
|
|
|
- pub async fn key_gen(&self) -> Result<()> {
|
|
|
+ pub fn get_own_coins(&self) -> Result<Vec<OwnCoin>> {
|
|
|
+ self.wallet.get_own_coins()
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn confirm_spend_coin(&self, coin: &Coin) -> Result<()> {
|
|
|
+ self.wallet.confirm_spend_coin(coin)
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn key_gen(&self) -> Result<()> {
|
|
|
self.wallet.key_gen()
|
|
|
}
|
|
|
|
|
|
- pub async fn get_balances(&self) -> Result<Balances> {
|
|
|
+ pub fn get_balances(&self) -> Result<Balances> {
|
|
|
self.wallet.get_balances()
|
|
|
}
|
|
|
|
|
|
- pub async fn token_id_exists(&self, token_id: &jubjub::Fr) -> Result<bool> {
|
|
|
+ pub fn token_id_exists(&self, token_id: &jubjub::Fr) -> Result<bool> {
|
|
|
self.wallet.token_id_exists(token_id)
|
|
|
}
|
|
|
|
|
|
- pub async fn get_token_id(&self) -> Result<Vec<jubjub::Fr>> {
|
|
|
+ pub fn get_token_id(&self) -> Result<Vec<jubjub::Fr>> {
|
|
|
self.wallet.get_token_id()
|
|
|
}
|
|
|
}
|
|
|
@@ -533,7 +513,7 @@ impl std::fmt::Display for ClientFailed {
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
match self {
|
|
|
ClientFailed::NotEnoughValue(i) => {
|
|
|
- write!(f, "There is not enough value {}", i)
|
|
|
+ write!(f, "There is no enough value {}", i)
|
|
|
}
|
|
|
ClientFailed::InvalidAddress(i) => {
|
|
|
write!(f, "Invalid Address {}", i)
|
|
|
@@ -552,8 +532,13 @@ impl std::fmt::Display for ClientFailed {
|
|
|
ClientFailed::EmptyPassword => f.write_str("Password is empty. Cannot create database"),
|
|
|
ClientFailed::WalletInitialized => f.write_str("Wallet already initalized"),
|
|
|
ClientFailed::KeyExists => f.write_str("Keypair already exists"),
|
|
|
+
|
|
|
ClientFailed::ClientError(i) => {
|
|
|
- write!(f, "ClientError: {}", i)
|
|
|
+ write!(f, "{}", i)
|
|
|
+ }
|
|
|
+
|
|
|
+ ClientFailed::VerifyError(i) => {
|
|
|
+ write!(f, "Verify error: {}", i)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -565,4 +550,10 @@ impl From<super::error::Error> for ClientFailed {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl From<crate::state::VerifyFailed> for ClientFailed {
|
|
|
+ fn from(err: crate::state::VerifyFailed) -> ClientFailed {
|
|
|
+ ClientFailed::VerifyError(err.to_string())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
pub type ClientResult<T> = std::result::Result<T, ClientFailed>;
|