mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
minor changes in client.rs
This commit is contained in:
@@ -122,9 +122,6 @@ path = "src/bin/cashierd.rs"
|
||||
name = "darkfid"
|
||||
path = "src/bin/darkfid.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "darkfid-old"
|
||||
path = "src/bin/darkfid-old.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "drk"
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
use drk::blockchain::{rocks::columns, Rocks, RocksColumn, Slab};
|
||||
use drk::cli::TransferParams;
|
||||
use drk::cli::{Config, DarkfidCli, DarkfidConfig};
|
||||
use drk::crypto::{
|
||||
load_params,
|
||||
merkle::{CommitmentTree, IncrementalWitness},
|
||||
merkle_node::MerkleNode,
|
||||
note::{EncryptedNote, Note},
|
||||
nullifier::Nullifier,
|
||||
save_params, setup_mint_prover, setup_spend_prover,
|
||||
};
|
||||
use drk::rpc::adapters::user_adapter::UserAdapter;
|
||||
use drk::rpc::jsonserver;
|
||||
use drk::serial::{deserialize, Decodable};
|
||||
use drk::service::{CashierClient, GatewayClient, GatewaySlabsSubscriber};
|
||||
use drk::state::{state_transition, ProgramState, StateUpdate};
|
||||
use drk::util::{join_config_path, prepare_transaction};
|
||||
use drk::wallet::{WalletDb, WalletPtr};
|
||||
use drk::{tx, Result};
|
||||
|
||||
use async_executor::Executor;
|
||||
use bellman::groth16;
|
||||
use bls12_381::Bls12;
|
||||
use easy_parallel::Parallel;
|
||||
use ff::Field;
|
||||
use log::*;
|
||||
use rand::rngs::OsRng;
|
||||
use rusqlite::Connection;
|
||||
|
||||
use async_std::sync::Arc;
|
||||
use futures::FutureExt;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct State {
|
||||
// The entire merkle tree state
|
||||
tree: CommitmentTree<MerkleNode>,
|
||||
// List of all previous and the current merkle roots
|
||||
// This is the hashed value of all the children.
|
||||
merkle_roots: RocksColumn<columns::MerkleRoots>,
|
||||
// Nullifiers prevent double spending
|
||||
nullifiers: RocksColumn<columns::Nullifiers>,
|
||||
// Mint verifying key used by ZK
|
||||
mint_pvk: groth16::PreparedVerifyingKey<Bls12>,
|
||||
// Spend verifying key used by ZK
|
||||
spend_pvk: groth16::PreparedVerifyingKey<Bls12>,
|
||||
// Public key of the cashier
|
||||
// List of all our secret keys
|
||||
wallet: WalletPtr,
|
||||
}
|
||||
|
||||
impl ProgramState for State {
|
||||
fn is_valid_cashier_public_key(&self, _public: &jubjub::SubgroupPoint) -> bool {
|
||||
let conn = Connection::open(&self.wallet.path).expect("Failed to connect to database");
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT key_public FROM cashier WHERE key_public IN (SELECT key_public)")
|
||||
.expect("Cannot generate statement.");
|
||||
stmt.exists([1i32]).expect("Failed to read database")
|
||||
// do actual validity check
|
||||
}
|
||||
|
||||
fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool {
|
||||
self.merkle_roots
|
||||
.key_exist(*merkle_root)
|
||||
.expect("couldn't check if the merkle_root valid")
|
||||
}
|
||||
|
||||
fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
|
||||
self.nullifiers
|
||||
.key_exist(nullifier.repr)
|
||||
.expect("couldn't check if nullifier exists")
|
||||
}
|
||||
|
||||
// load from disk
|
||||
fn mint_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
|
||||
&self.mint_pvk
|
||||
}
|
||||
|
||||
fn spend_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
|
||||
&self.spend_pvk
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn apply(&mut self, update: StateUpdate) -> Result<()> {
|
||||
// Extend our list of nullifiers with the ones from the update
|
||||
for nullifier in update.nullifiers {
|
||||
self.nullifiers.put(nullifier, vec![] as Vec<u8>)?;
|
||||
}
|
||||
|
||||
// Update merkle tree and witnesses
|
||||
for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) {
|
||||
// Add the new coins to the merkle tree
|
||||
let node = MerkleNode::from_coin(&coin);
|
||||
self.tree.append(node).expect("Append to merkle tree");
|
||||
|
||||
// Keep track of all merkle roots that have existed
|
||||
self.merkle_roots.put(self.tree.root(), vec![] as Vec<u8>)?;
|
||||
|
||||
// Also update all the coin witnesses
|
||||
for witness in self.wallet.witnesses.lock().await.iter_mut() {
|
||||
witness.append(node).expect("append to witness");
|
||||
}
|
||||
|
||||
if let Some((note, secret)) = self.try_decrypt_note(enc_note).await {
|
||||
// We need to keep track of the witness for this coin.
|
||||
// This allows us to prove inclusion of the coin in the merkle tree with ZK.
|
||||
// Just as we update the merkle tree with every new coin, so we do the same with
|
||||
// the witness.
|
||||
|
||||
// Derive the current witness from the current tree.
|
||||
// This is done right after we add our coin to the tree (but before any other
|
||||
// coins are added)
|
||||
|
||||
// Make a new witness for this coin
|
||||
let witness = IncrementalWitness::from_tree(&self.tree);
|
||||
|
||||
self.wallet
|
||||
.put_own_coins(coin.clone(), note.clone(), witness.clone(), secret)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_decrypt_note(&self, ciphertext: EncryptedNote) -> Option<(Note, jubjub::Fr)> {
|
||||
let secret = self.wallet.get_private().ok()?;
|
||||
match ciphertext.decrypt(&secret) {
|
||||
Ok(note) => {
|
||||
// ... and return the decrypted note for this coin.
|
||||
return Some((note, secret.clone()));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
// We weren't able to decrypt the note with our key.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
//pub async fn subscribe(
|
||||
// gateway_slabs_sub: GatewaySlabsSubscriber,
|
||||
// mut state: State,
|
||||
//) -> Result<()> {
|
||||
//}
|
||||
|
||||
pub async fn futures_broker(
|
||||
client: &mut GatewayClient,
|
||||
cashier_client: &mut CashierClient,
|
||||
state: &mut State,
|
||||
secret: jubjub::Fr,
|
||||
mint_params: bellman::groth16::Parameters<Bls12>,
|
||||
spend_params: bellman::groth16::Parameters<Bls12>,
|
||||
gateway_slabs_sub: async_channel::Receiver<Slab>,
|
||||
deposit_req: async_channel::Receiver<jubjub::SubgroupPoint>,
|
||||
deposit_rep: async_channel::Sender<Option<bitcoin::util::address::Address>>,
|
||||
withdraw_req: async_channel::Receiver<String>,
|
||||
withdraw_rep: async_channel::Sender<Option<jubjub::SubgroupPoint>>,
|
||||
publish_tx_recv: async_channel::Receiver<TransferParams>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
futures::select! {
|
||||
slab = gateway_slabs_sub.recv().fuse() => {
|
||||
let slab = slab?;
|
||||
let tx = tx::Transaction::decode(&slab.get_payload()[..])?;
|
||||
let update = state_transition(state, tx)?;
|
||||
state.apply(update).await?;
|
||||
}
|
||||
deposit_addr = deposit_req.recv().fuse() => {
|
||||
let btc_public = cashier_client.get_address(deposit_addr?).await?;
|
||||
deposit_rep.send(btc_public).await?;
|
||||
}
|
||||
withdraw_addr = withdraw_req.recv().fuse() => {
|
||||
let drk_public = cashier_client.withdraw(withdraw_addr?).await?;
|
||||
withdraw_rep.send(drk_public).await?;
|
||||
}
|
||||
transfer_params = publish_tx_recv.recv().fuse() => {
|
||||
let transfer_params = transfer_params?;
|
||||
|
||||
let address = bs58::decode(transfer_params.pub_key).into_vec()?;
|
||||
let address: jubjub::SubgroupPoint = deserialize(&address)?;
|
||||
|
||||
let own_coins = state.wallet.get_own_coins()?;
|
||||
|
||||
let slab = prepare_transaction(
|
||||
state,
|
||||
secret.clone(),
|
||||
mint_params.clone(),
|
||||
spend_params.clone(),
|
||||
address,
|
||||
transfer_params.amount,
|
||||
own_coins
|
||||
)?;
|
||||
|
||||
client.put_slab(slab).await.expect("put slab");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn start(executor: Arc<Executor<'_>>, config: Arc<DarkfidConfig>) -> Result<()> {
|
||||
let connect_addr: SocketAddr = config.connect_url.parse()?;
|
||||
let sub_addr: SocketAddr = config.subscriber_url.parse()?;
|
||||
let cashier_addr: SocketAddr = config.cashier_url.parse()?;
|
||||
let database_path = config.database_path.clone();
|
||||
let walletdb_path = config.walletdb_path.clone();
|
||||
|
||||
let database_path = join_config_path(&PathBuf::from(database_path))?;
|
||||
let walletdb_path = join_config_path(&PathBuf::from(walletdb_path))?;
|
||||
|
||||
let rocks = Rocks::new(&database_path)?;
|
||||
|
||||
let rocks2 = rocks.clone();
|
||||
let slabstore = RocksColumn::<columns::Slabs>::new(rocks2.clone());
|
||||
|
||||
// Auto create trusted ceremony parameters if they don't exist
|
||||
if !Path::new("mint.params").exists() {
|
||||
let params = setup_mint_prover();
|
||||
save_params("mint.params", ¶ms)?;
|
||||
}
|
||||
if !Path::new("spend.params").exists() {
|
||||
let params = setup_spend_prover();
|
||||
save_params("spend.params", ¶ms)?;
|
||||
}
|
||||
|
||||
// Load trusted setup parameters
|
||||
let (mint_params, mint_pvk) = load_params("mint.params")?;
|
||||
let (spend_params, spend_pvk) = load_params("spend.params")?;
|
||||
|
||||
//let cashier_secret = jubjub::Fr::random(&mut OsRng);
|
||||
//let cashier_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * cashier_secret;
|
||||
|
||||
// wallet secret key
|
||||
let secret = jubjub::Fr::random(&mut OsRng);
|
||||
// wallet public key
|
||||
let _public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
|
||||
|
||||
let merkle_roots = RocksColumn::<columns::MerkleRoots>::new(rocks.clone());
|
||||
let nullifiers = RocksColumn::<columns::Nullifiers>::new(rocks);
|
||||
|
||||
let wallet = Arc::new(WalletDb::new(&walletdb_path, config.password.clone())?);
|
||||
|
||||
let ex = executor.clone();
|
||||
|
||||
let mut state = State {
|
||||
tree: CommitmentTree::empty(),
|
||||
merkle_roots,
|
||||
nullifiers,
|
||||
mint_pvk,
|
||||
spend_pvk,
|
||||
wallet: wallet.clone(),
|
||||
};
|
||||
|
||||
// create gateway client
|
||||
debug!(target: "Client", "Creating client");
|
||||
let mut client = GatewayClient::new(connect_addr, sub_addr, slabstore)?;
|
||||
|
||||
// create cashier client
|
||||
debug!(target: "Cashier Client", "Creating cashier client");
|
||||
let mut cashier_client = CashierClient::new(cashier_addr)?;
|
||||
|
||||
debug!(target: "Gateway", "Start subscriber");
|
||||
// start subscribing
|
||||
let gateway_slabs_sub: GatewaySlabsSubscriber =
|
||||
client.start_subscriber(executor.clone()).await?;
|
||||
|
||||
// channels to request transfer from adapter
|
||||
let (publish_tx_send, publish_tx_recv) = async_channel::unbounded::<TransferParams>();
|
||||
|
||||
// channels to request deposit from adapter, send DRK key and receive BTC key
|
||||
let (deposit_req_send, deposit_req_recv) = async_channel::unbounded::<jubjub::SubgroupPoint>();
|
||||
let (deposit_rep_send, deposit_rep_recv) =
|
||||
async_channel::unbounded::<Option<bitcoin::util::address::Address>>();
|
||||
|
||||
// channel to request withdraw from adapter, send BTC key and receive DRK key
|
||||
let (withdraw_req_send, withdraw_req_recv) = async_channel::unbounded::<String>();
|
||||
let (withdraw_rep_send, withdraw_rep_recv) =
|
||||
async_channel::unbounded::<Option<jubjub::SubgroupPoint>>();
|
||||
|
||||
// start gateway client
|
||||
debug!(target: "fn::start client", "start() Client started");
|
||||
client.start().await?;
|
||||
cashier_client.start().await?;
|
||||
|
||||
let futures_broker_task = executor.spawn(async move {
|
||||
futures_broker(
|
||||
&mut client,
|
||||
&mut cashier_client,
|
||||
&mut state,
|
||||
secret.clone(),
|
||||
mint_params.clone(),
|
||||
spend_params.clone(),
|
||||
gateway_slabs_sub.clone(),
|
||||
deposit_req_recv.clone(),
|
||||
deposit_rep_send.clone(),
|
||||
withdraw_req_recv.clone(),
|
||||
withdraw_rep_send.clone(),
|
||||
publish_tx_recv.clone(),
|
||||
)
|
||||
.await?;
|
||||
Ok::<(), drk::Error>(())
|
||||
});
|
||||
|
||||
let adapter = Arc::new(UserAdapter::new(
|
||||
wallet.clone(),
|
||||
publish_tx_send,
|
||||
(deposit_req_send, deposit_rep_recv),
|
||||
(withdraw_req_send, withdraw_rep_recv),
|
||||
)?);
|
||||
|
||||
let rpc_url: std::net::SocketAddr = config.rpc_url.parse()?;
|
||||
|
||||
// start the rpc server
|
||||
let io = Arc::new(adapter.handle_input()?);
|
||||
jsonserver::start(ex.clone(), rpc_url, io).await?;
|
||||
|
||||
futures_broker_task.cancel().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let options = Arc::new(DarkfidCli::load()?);
|
||||
|
||||
let config_path: PathBuf;
|
||||
|
||||
match options.config.as_ref() {
|
||||
Some(path) => {
|
||||
config_path = path.to_owned();
|
||||
}
|
||||
None => {
|
||||
config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
|
||||
}
|
||||
}
|
||||
|
||||
let config: DarkfidConfig = if Path::new(&config_path).exists() {
|
||||
Config::<DarkfidConfig>::load(config_path)?
|
||||
} else {
|
||||
Config::<DarkfidConfig>::load_default(config_path)?
|
||||
};
|
||||
|
||||
let config = Arc::new(config);
|
||||
|
||||
let ex = Arc::new(Executor::new());
|
||||
let (signal, shutdown) = async_channel::unbounded::<()>();
|
||||
|
||||
{
|
||||
use simplelog::*;
|
||||
let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
|
||||
|
||||
let debug_level = if options.verbose {
|
||||
LevelFilter::Debug
|
||||
} else {
|
||||
LevelFilter::Off
|
||||
};
|
||||
|
||||
let log_path = config.log_path.clone();
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
|
||||
WriteLogger::new(
|
||||
LevelFilter::Debug,
|
||||
Config::default(),
|
||||
std::fs::File::create(log_path).unwrap(),
|
||||
),
|
||||
])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let ex2 = ex.clone();
|
||||
|
||||
let (_, result) = Parallel::new()
|
||||
// Run four executor threads.
|
||||
.each(0..3, |_| smol::future::block_on(ex.run(shutdown.recv())))
|
||||
// Run the main future on the current thread.
|
||||
.finish(|| {
|
||||
smol::future::block_on(async move {
|
||||
start(ex2, config).await?;
|
||||
drop(signal);
|
||||
Ok::<(), drk::Error>(())
|
||||
})
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
@@ -29,11 +29,16 @@ async fn start(executor: Arc<Executor<'_>>, config: Arc<DarkfidConfig>) -> Resul
|
||||
|
||||
let rocks = Rocks::new(&database_path)?;
|
||||
|
||||
// wallet secret key
|
||||
let secret = jubjub::Fr::random(&mut OsRng);
|
||||
|
||||
let wallet = Arc::new(WalletDb::new(&walletdb_path, config.password.clone())?);
|
||||
|
||||
// wallet secret key
|
||||
let secret: jubjub::Fr;
|
||||
if let Some(prv) = wallet.get_private().ok() {
|
||||
secret = prv;
|
||||
} else {
|
||||
secret = jubjub::Fr::random(&mut OsRng);
|
||||
}
|
||||
|
||||
let mut client = Client::new(
|
||||
secret,
|
||||
rocks,
|
||||
|
||||
@@ -10,10 +10,10 @@ use crate::crypto::{
|
||||
};
|
||||
use crate::rpc::adapters::user_adapter::UserAdapter;
|
||||
use crate::rpc::jsonserver;
|
||||
use crate::serial::Encodable;
|
||||
use crate::serial::{deserialize, Decodable};
|
||||
use crate::service::{CashierClient, GatewayClient, GatewaySlabsSubscriber};
|
||||
use crate::state::{state_transition, ProgramState, StateUpdate};
|
||||
use crate::util::prepare_transaction;
|
||||
use crate::wallet::WalletPtr;
|
||||
use crate::{tx, Result};
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Client {
|
||||
let nullifiers = RocksColumn::<columns::Nullifiers>::new(rocks);
|
||||
|
||||
let mint_params_path = params_paths.0.to_str().unwrap_or("mint.params");
|
||||
let spend_params_path = params_paths.1.to_str().unwrap_or("spend.params");
|
||||
let spend_params_path = params_paths.1.to_str().unwrap_or("spend.params");
|
||||
|
||||
// Auto create trusted ceremony parameters if they don't exist
|
||||
if !params_paths.0.exists() {
|
||||
@@ -211,24 +211,57 @@ impl Client {
|
||||
let address = bs58::decode(transfer_params.pub_key).into_vec()?;
|
||||
let address: jubjub::SubgroupPoint = deserialize(&address)?;
|
||||
|
||||
let own_coins = wallet.get_own_coins()?;
|
||||
|
||||
let slab = prepare_transaction(
|
||||
&self.state,
|
||||
self.secret.clone(),
|
||||
self.mint_params.clone(),
|
||||
self.spend_params.clone(),
|
||||
let slab = self.prepare_transaction(
|
||||
address,
|
||||
transfer_params.amount,
|
||||
own_coins
|
||||
wallet.clone()
|
||||
)?;
|
||||
|
||||
self.gateway.put_slab(slab).await.expect("put slab");
|
||||
self.gateway.put_slab(slab).await?;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_transaction(
|
||||
&self,
|
||||
address: jubjub::SubgroupPoint,
|
||||
amount: f64,
|
||||
wallet: WalletPtr,
|
||||
) -> Result<Slab> {
|
||||
let own_coins = wallet.get_own_coins()?;
|
||||
let witness = &own_coins[0].3;
|
||||
let merkle_path = witness.path().unwrap();
|
||||
|
||||
// Construct a new tx spending the coin
|
||||
let builder = tx::TransactionBuilder {
|
||||
clear_inputs: vec![],
|
||||
inputs: vec![tx::TransactionBuilderInputInfo {
|
||||
merkle_path,
|
||||
secret: self.secret.clone(),
|
||||
note: own_coins[0].1.clone(),
|
||||
}],
|
||||
// We can add more outputs to this list.
|
||||
// The only constraint is that sum(value in) == sum(value out)
|
||||
outputs: vec![tx::TransactionBuilderOutputInfo {
|
||||
value: amount as u64,
|
||||
asset_id: 1,
|
||||
public: address,
|
||||
}],
|
||||
};
|
||||
// Build the tx
|
||||
let mut tx_data = vec![];
|
||||
{
|
||||
let tx = builder.build(&self.mint_params, &self.spend_params);
|
||||
tx.encode(&mut tx_data).expect("encode tx");
|
||||
}
|
||||
|
||||
// build slab from the transaction
|
||||
let slab = Slab::new(tx_data);
|
||||
return Ok(slab);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
|
||||
46
src/util.rs
46
src/util.rs
@@ -1,12 +1,5 @@
|
||||
use crate::blockchain::Slab;
|
||||
use crate::crypto::OwnCoins;
|
||||
use crate::serial::Encodable;
|
||||
use crate::state::ProgramState;
|
||||
use crate::tx;
|
||||
use crate::Result;
|
||||
|
||||
use bls12_381::Bls12;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn join_config_path(file: &PathBuf) -> Result<PathBuf> {
|
||||
@@ -25,42 +18,3 @@ pub fn join_config_path(file: &PathBuf) -> Result<PathBuf> {
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn prepare_transaction(
|
||||
_state: &dyn ProgramState,
|
||||
secret: jubjub::Fr,
|
||||
mint_params: bellman::groth16::Parameters<Bls12>,
|
||||
spend_params: bellman::groth16::Parameters<Bls12>,
|
||||
address: jubjub::SubgroupPoint,
|
||||
amount: f64,
|
||||
own_coins: OwnCoins,
|
||||
) -> Result<Slab> {
|
||||
let witness = &own_coins[0].3;
|
||||
let merkle_path = witness.path().unwrap();
|
||||
|
||||
// Construct a new tx spending the coin
|
||||
let builder = tx::TransactionBuilder {
|
||||
clear_inputs: vec![],
|
||||
inputs: vec![tx::TransactionBuilderInputInfo {
|
||||
merkle_path,
|
||||
secret: secret.clone(),
|
||||
note: own_coins[0].1.clone(),
|
||||
}],
|
||||
// We can add more outputs to this list.
|
||||
// The only constraint is that sum(value in) == sum(value out)
|
||||
outputs: vec![tx::TransactionBuilderOutputInfo {
|
||||
value: amount as u64,
|
||||
asset_id: 1,
|
||||
public: address,
|
||||
}],
|
||||
};
|
||||
// Build the tx
|
||||
let mut tx_data = vec![];
|
||||
{
|
||||
let tx = builder.build(&mint_params, &spend_params);
|
||||
tx.encode(&mut tx_data).expect("encode tx");
|
||||
}
|
||||
|
||||
// build slab from the transaction
|
||||
let slab = Slab::new(tx_data);
|
||||
return Ok(slab);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user