lib: Move wallet module out of the node module.

This commit is contained in:
parazyd
2022-04-13 12:43:10 +02:00
parent 6423e0c377
commit f42e935700
12 changed files with 59 additions and 37 deletions

View File

@@ -5,6 +5,10 @@ use log::{debug, info, warn};
use smol::Executor;
use url::Url;
use super::{
service::GatewayClient,
state::{state_transition, State, StateUpdate},
};
use crate::{
blockchain::{rocks::columns, Rocks, RocksColumn, Slab},
crypto::{
@@ -18,17 +22,12 @@ use crate::{
},
tx,
util::serial::{Decodable, Encodable},
zk::circuit::{MintContract, SpendContract},
Result,
};
use super::{
service::GatewayClient,
state::{state_transition, State, StateUpdate},
wallet::{
cashierdb::CashierDbPtr,
walletdb::{Balances, WalletPtr},
},
zk::circuit::{MintContract, SpendContract},
Result,
};
#[derive(Debug, Clone, thiserror::Error)]
@@ -95,7 +94,7 @@ impl Client {
if wallet.get_default_keypair().await.is_err() {
// Generate a new keypair if we don't have any.
if wallet.get_keypairs().await?.is_empty() {
wallet.key_gen().await?;
wallet.keygen().await?;
}
// set the first keypair as the default one
wallet.set_default_keypair(&wallet.get_keypairs().await?[0].public).await?;
@@ -397,8 +396,9 @@ impl Client {
Ok(())
}
pub async fn key_gen(&self) -> Result<()> {
self.wallet.key_gen().await
pub async fn keygen(&self) -> Result<()> {
let _ = self.wallet.keygen().await?;
Ok(())
}
pub async fn get_balances(&self) -> Result<Balances> {

View File

@@ -1,6 +1,3 @@
pub mod client;
pub mod service;
pub mod state;
#[cfg(feature = "wallet")]
pub mod wallet;

View File

@@ -14,11 +14,10 @@ use crate::{
},
error,
tx::Transaction,
wallet::walletdb::WalletPtr,
Result,
};
use super::wallet::walletdb::WalletPtr;
pub trait ProgramState {
fn is_valid_cashier_public_key(&self, public: &PublicKey) -> bool;
fn is_valid_merkle(&self, merkle: &MerkleNode) -> bool;

View File

@@ -1,576 +0,0 @@
use std::{fs::create_dir_all, path::Path, str::FromStr, time::Duration};
use async_std::sync::Arc;
use incrementalmerkletree::bridgetree::BridgeTree;
use log::{debug, error, info, LevelFilter};
use sqlx::{
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions},
ConnectOptions, Row, SqlitePool,
};
use super::wallet_api::WalletApi;
use crate::{
crypto::{
keypair::{Keypair, PublicKey, SecretKey},
merkle_node::MerkleNode,
types::DrkTokenId,
},
node::client::ClientFailed,
util::NetworkName,
Error, Result,
};
pub type CashierDbPtr = Arc<CashierDb>;
#[derive(Debug, Clone)]
pub struct TokenKey {
pub public_key: Vec<u8>,
pub secret_key: Vec<u8>,
}
pub struct WithdrawToken {
pub token_public_key: Vec<u8>,
pub network: NetworkName,
pub token_id: DrkTokenId,
pub mint_address: String,
}
pub struct DepositToken {
pub drk_public_key: PublicKey,
pub token_key: TokenKey,
pub token_id: DrkTokenId,
pub mint_address: String,
}
pub struct CashierDb {
pub conn: SqlitePool,
}
impl WalletApi for CashierDb {}
impl CashierDb {
pub async fn new(path: &str, password: &str) -> Result<CashierDbPtr> {
debug!("new() Constructor called");
if password.trim().is_empty() {
error!("Password is empty. You must set a password to use the wallet.");
return Err(Error::from(ClientFailed::EmptyPassword))
}
if path != "sqlite::memory:" {
let p = Path::new(path.strip_prefix("sqlite://").unwrap());
if let Some(dirname) = p.parent() {
info!("Creating path to database: {}", dirname.display());
create_dir_all(&dirname)?;
}
}
let mut connect_opts = SqliteConnectOptions::from_str(path)?
.pragma("key", password.to_string())
.create_if_missing(true)
.journal_mode(SqliteJournalMode::Off);
connect_opts.log_statements(LevelFilter::Trace);
connect_opts.log_slow_statements(LevelFilter::Trace, Duration::from_micros(10));
let conn = SqlitePoolOptions::new().connect_with(connect_opts).await?;
info!("Opened connection at path: {:?}", path);
Ok(Arc::new(CashierDb { conn }))
}
pub async fn init_db(&self) -> Result<()> {
let main_kps = include_str!("../../../script/sql/cashier_main_keypairs.sql");
let deposit_kps = include_str!("../../../script/sql/cashier_deposit_keypairs.sql");
let withdraw_kps = include_str!("../../../script/sql/cashier_withdraw_keypairs.sql");
let mut conn = self.conn.acquire().await?;
debug!("Initializing main keypairs table");
sqlx::query(main_kps).execute(&mut conn).await?;
debug!("Initializing deposit keypairs table");
sqlx::query(deposit_kps).execute(&mut conn).await?;
debug!("Initializing withdraw keypairs table");
sqlx::query(withdraw_kps).execute(&mut conn).await?;
Ok(())
}
pub async fn tree_gen(&self) -> Result<()> {
debug!("Attempting to generate merkle tree");
let mut conn = self.conn.acquire().await?;
match sqlx::query("SELECT * FROM tree").fetch_one(&mut conn).await {
Ok(_) => {
error!("Tree already exists");
Err(Error::from(ClientFailed::TreeExists))
}
Err(_) => {
let tree = BridgeTree::<MerkleNode, 32>::new(100);
self.put_tree(&tree).await?;
Ok(())
}
}
}
pub async fn get_tree(&self) -> Result<BridgeTree<MerkleNode, 32>> {
debug!("Getting merkle tree");
let mut conn = self.conn.acquire().await?;
let row = sqlx::query("SELECT tree FROM tree").fetch_one(&mut conn).await?;
let tree: BridgeTree<MerkleNode, 32> = bincode::deserialize(row.get("tree"))?;
Ok(tree)
}
pub async fn put_tree(&self, tree: &BridgeTree<MerkleNode, 32>) -> Result<()> {
debug!("Attempting to write merkle tree");
let mut conn = self.conn.acquire().await?;
let tree_bytes = bincode::serialize(tree)?;
sqlx::query("INSERT INTO tree(tree) VALUES (?1)")
.bind(tree_bytes)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn put_main_keys(&self, token_key: &TokenKey, network: &NetworkName) -> Result<()> {
debug!("Writing main keys into the database");
let network = self.get_value_serialized(network)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"INSERT INTO main_keypairs
(token_key_secret, token_key_public, network)
VALUES
(?1, ?2, ?3);",
)
.bind(token_key.secret_key.clone())
.bind(token_key.public_key.clone())
.bind(network)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_main_keys(&self, network: &NetworkName) -> Result<Vec<TokenKey>> {
debug!("Returning main keypairs");
let network = self.get_value_serialized(network)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT token_key_secret, token_key_public
FROM main_keypairs WHERE network = ?1;",
)
.bind(network)
.fetch_all(&mut conn)
.await?;
let mut keys = vec![];
for row in rows {
let secret_key = row.get("token_key_secret");
let public_key = row.get("token_key_public");
keys.push(TokenKey { secret_key, public_key })
}
Ok(keys)
}
pub async fn remove_withdraw_and_deposit_keys(&self) -> Result<()> {
debug!("Removing withdraw and deposit keys");
let mut conn = self.conn.acquire().await?;
sqlx::query("DROP TABLE deposit_keypairs;").execute(&mut conn).await?;
sqlx::query("DROP TABLE withdraw_keypairs;").execute(&mut conn).await?;
Ok(())
}
pub async fn put_withdraw_keys(
&self,
token_key_public: &[u8],
d_key_public: &PublicKey,
d_key_secret: &SecretKey,
network: &NetworkName,
token_id: &DrkTokenId,
mint_address: String,
) -> Result<()> {
debug!("Writing withdraw keys to database");
let public = self.get_value_serialized(d_key_public)?;
let secret = self.get_value_serialized(d_key_secret)?;
let network = self.get_value_serialized(network)?;
let token_id = self.get_value_serialized(token_id)?;
let confirm = self.get_value_serialized(&false)?;
let mint_address = self.get_value_serialized(&mint_address)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"INSERT INTO withdraw_keypairs
(token_key_public, d_key_secret, d_key_public,
network, token_id, mint_address, confirm)
VALUES
(?1, ?2, ?3, ?4, ?5, ?6, ?7);",
)
.bind(token_key_public)
.bind(secret)
.bind(public)
.bind(network)
.bind(token_id)
.bind(mint_address)
.bind(confirm)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn put_deposit_keys(
&self,
d_key_public: &PublicKey,
token_key_secret: &[u8],
token_key_public: &[u8],
network: &NetworkName,
token_id: &DrkTokenId,
mint_address: String,
) -> Result<()> {
debug!("Writing deposit keys to database");
let d_key_public = self.get_value_serialized(d_key_public)?;
let token_id = self.get_value_serialized(token_id)?;
let network = self.get_value_serialized(network)?;
let confirm = self.get_value_serialized(&false)?;
let mint_address = self.get_value_serialized(&mint_address)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"INSERT INTO deposit_keypairs
(d_key_public, token_key_secret, token_key_public,
network, token_id, mint_address, confirm)
VALUES
(?1, ?2, ?3, ?4, ?5, ?6, ?7);",
)
.bind(d_key_public)
.bind(token_key_secret)
.bind(token_key_public)
.bind(network)
.bind(token_id)
.bind(mint_address)
.bind(confirm)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_withdraw_private_keys(&self) -> Result<Vec<SecretKey>> {
debug!("Getting withdraw private keys");
let confirm = self.get_value_serialized(&false)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT d_key_secret FROM withdraw_keypairs
WHERE confirm = ?1",
)
.bind(confirm)
.fetch_all(&mut conn)
.await?;
let mut secret_keys = vec![];
for row in rows {
let key: SecretKey = self.get_value_deserialized(row.get("d_key_secret"))?;
secret_keys.push(key);
}
Ok(secret_keys)
}
pub async fn get_withdraw_token_public_key_by_dkey_public(
&self,
pubkey: &PublicKey,
) -> Result<Option<WithdrawToken>> {
debug!("Get token address by pubkey");
let d_key_public = self.get_value_serialized(pubkey)?;
let confirm = self.get_value_serialized(&false)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT token_key_public, network, token_id, mint_address
FROM withdraw_keypairs
WHERE d_key_public = ?1
AND confirm = ?2;",
)
.bind(d_key_public)
.bind(confirm)
.fetch_all(&mut conn)
.await?;
let mut token_addrs = vec![];
for row in rows {
let token_public_key = row.get("token_key_public");
let network = self.get_value_deserialized(row.get("network"))?;
let token_id = self.get_value_deserialized(row.get("token_id"))?;
let mint_address = self.get_value_deserialized(row.get("mint_address"))?;
token_addrs.push(WithdrawToken { token_public_key, network, token_id, mint_address });
}
Ok(token_addrs.pop())
}
pub async fn get_deposit_token_keys_by_dkey_public(
&self,
d_key_public: &PublicKey,
network: &NetworkName,
) -> Result<Vec<TokenKey>> {
debug!("Checking for existing dkey");
let d_key_public = self.get_value_serialized(d_key_public)?;
let network = self.get_value_serialized(network)?;
let confirm = self.get_value_serialized(&false)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT token_key_secret, token_key_public
FROM deposit_keypairs
WHERE d_key_public = ?1
AND network = ?2
AND confirm = ?3;",
)
.bind(d_key_public)
.bind(network)
.bind(confirm)
.fetch_all(&mut conn)
.await?;
let mut keys = vec![];
for row in rows {
let secret_key = row.get("token_key_secret");
let public_key = row.get("token_key_public");
keys.push(TokenKey { secret_key, public_key });
}
Ok(keys)
}
pub async fn get_withdraw_keys_by_token_public_key(
&self,
token_key_public: &[u8],
network: &NetworkName,
) -> Result<Option<Keypair>> {
debug!("Checking for existing token address");
let confirm = self.get_value_serialized(&false)?;
let network = self.get_value_serialized(network)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT d_key_secret, d_key_public FROM withdraw_keypairs
WHERE token_key_public = ?1
AND network = ?2
AND confirm = ?3;",
)
.bind(token_key_public)
.bind(network)
.bind(confirm)
.fetch_all(&mut conn)
.await?;
let mut keypairs = vec![];
for row in rows {
let public = self.get_value_deserialized(row.get("d_key_public"))?;
let secret = self.get_value_deserialized(row.get("d_key_secret"))?;
keypairs.push(Keypair { public, secret });
}
Ok(keypairs.pop())
}
pub async fn confirm_withdraw_key_record(
&self,
token_address: &[u8],
network: &NetworkName,
) -> Result<()> {
debug!("Confirm withdraw keys");
let network = self.get_value_serialized(network)?;
let confirm = self.get_value_serialized(&true)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"UPDATE withdraw_keypairs
SET confirm = ?1
WHERE token_key_public = ?2
AND network = ?3;",
)
.bind(confirm)
.bind(token_address)
.bind(network)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn confirm_deposit_key_record(
&self,
d_key_public: &PublicKey,
network: &NetworkName,
) -> Result<()> {
debug!("Confirm deposit keys");
let network = self.get_value_serialized(network)?;
let confirm = self.get_value_serialized(&true)?;
let d_key_public = self.get_value_serialized(d_key_public)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"UPDATE deposit_keypairs
SET confirm = ?1
WHERE d_key_public = ?2
AND network = ?3;",
)
.bind(confirm)
.bind(d_key_public)
.bind(network)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_deposit_token_keys_by_network(
&self,
network: &NetworkName,
) -> Result<Vec<DepositToken>> {
debug!("Checking for existing dkey");
let network = self.get_value_serialized(network)?;
let confirm = self.get_value_serialized(&false)?;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query(
"SELECT d_key_public, token_key_secret, token_key_public, token_id, mint_address
FROM deposit_keypairs
WHERE network = ?1
AND confirm = ?2;",
)
.bind(network)
.bind(confirm)
.fetch_all(&mut conn)
.await?;
let mut keys = vec![];
for row in rows {
let drk_public_key = self.get_value_deserialized(row.get("d_key_public"))?;
let secret_key = row.get("token_key_secret");
let public_key = row.get("token_key_public");
let token_id = self.get_value_deserialized(row.get("token_id"))?;
let mint_address = self.get_value_deserialized(row.get("mint_address"))?;
keys.push(DepositToken {
drk_public_key,
token_key: TokenKey { secret_key, public_key },
token_id,
mint_address,
});
}
Ok(keys)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::serial::serialize;
use group::ff::Field;
use rand::rngs::OsRng;
const WPASS: &str = "darkfi";
#[async_std::test]
async fn test_cashierdb() -> Result<()> {
let wallet = CashierDb::new("sqlite::memory:", WPASS).await?;
// init_db()
wallet.init_db().await?;
// BTC testnet address
let token_addr_secret = serialize(&String::from("2222222222222222222222222222222222"));
let token_addr_public = serialize(&String::from("mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk"));
let keypair = Keypair::random(&mut OsRng);
let token_id = DrkTokenId::random(&mut OsRng);
let network = NetworkName::Bitcoin;
// put_main_keys()
wallet
.put_main_keys(
&TokenKey {
secret_key: token_addr_secret.clone(),
public_key: token_addr_public.clone(),
},
&network,
)
.await?;
// get_main_keys()
let keys = wallet.get_main_keys(&network).await?;
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].secret_key, token_addr_secret);
assert_eq!(keys[0].public_key, token_addr_public);
// put_deposit_keys()
wallet
.put_deposit_keys(
&keypair.public,
&token_addr_secret,
&token_addr_public,
&network,
&token_id,
String::new(),
)
.await?;
// get_deposit_token_keys_by_dkey_public()
let keys = wallet.get_deposit_token_keys_by_dkey_public(&keypair.public, &network).await?;
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].secret_key, token_addr_secret);
assert_eq!(keys[0].public_key, token_addr_public);
// get_deposit_token_keys_by_network()
let resumed_keys = wallet.get_deposit_token_keys_by_network(&network).await?;
assert_eq!(resumed_keys[0].drk_public_key, keypair.public);
assert_eq!(resumed_keys[0].token_key.secret_key, token_addr_secret);
assert_eq!(resumed_keys[0].token_key.public_key, token_addr_public);
assert_eq!(resumed_keys[0].token_id, token_id);
// confirm_deposit_key_record()
wallet.confirm_deposit_key_record(&keypair.public, &network).await?;
let keys = wallet.get_deposit_token_keys_by_dkey_public(&keypair.public, &network).await?;
assert_eq!(keys.len(), 0);
// put_withdraw_keys()
wallet
.put_withdraw_keys(
&token_addr_public,
&keypair.public,
&keypair.secret,
&network,
&token_id,
String::new(),
)
.await?;
// get_withdraw_keys_by_token_public_key()
let addr =
wallet.get_withdraw_keys_by_token_public_key(&token_addr_public, &network).await?;
assert!(addr.is_some());
// confirm_withdraw_key_record()
wallet.confirm_withdraw_key_record(&token_addr_public, &network).await?;
let addr =
wallet.get_withdraw_keys_by_token_public_key(&token_addr_public, &network).await?;
assert!(addr.is_none());
Ok(())
}
}

View File

@@ -1,3 +0,0 @@
pub mod cashierdb;
pub mod wallet_api;
pub mod walletdb;

View File

@@ -1,16 +0,0 @@
use crate::{
util::serial::{deserialize, serialize, Decodable, Encodable},
Result,
};
pub trait WalletApi {
fn get_value_serialized<T: Encodable>(&self, data: &T) -> Result<Vec<u8>> {
let v = serialize(data);
Ok(v)
}
fn get_value_deserialized<D: Decodable>(&self, key: Vec<u8>) -> Result<D> {
let v: D = deserialize(&key)?;
Ok(v)
}
}

View File

@@ -1,503 +0,0 @@
use std::{fs::create_dir_all, path::Path, str::FromStr, time::Duration};
use async_std::sync::Arc;
use incrementalmerkletree::bridgetree::BridgeTree;
use log::{debug, error, info, LevelFilter};
use rand::rngs::OsRng;
use sqlx::{
sqlite::{SqliteConnectOptions, SqliteJournalMode},
ConnectOptions, Row, SqlitePool,
};
use crate::{
crypto::{
coin::Coin,
keypair::{Keypair, PublicKey, SecretKey},
merkle_node::MerkleNode,
note::Note,
nullifier::Nullifier,
types::DrkTokenId,
OwnCoin, OwnCoins,
},
node::client::ClientFailed,
util::serial::serialize,
Error, Result,
};
use super::wallet_api::WalletApi;
pub type WalletPtr = Arc<WalletDb>;
#[derive(Clone, Debug)]
pub struct Balance {
pub token_id: DrkTokenId,
pub value: u64,
pub nullifier: Nullifier,
}
#[derive(Clone, Debug)]
pub struct Balances {
pub list: Vec<Balance>,
}
pub struct WalletDb {
pub conn: SqlitePool,
}
impl WalletApi for WalletDb {}
impl WalletDb {
pub async fn new(path: &str, password: &str) -> Result<WalletPtr> {
if password.trim().is_empty() {
error!("Password is empty. You must set a password to use the wallet.");
return Err(Error::from(ClientFailed::EmptyPassword))
}
if path != "sqlite::memory:" {
let p = Path::new(path.strip_prefix("sqlite://").unwrap());
if let Some(dirname) = p.parent() {
info!("Creating path to database: {}", dirname.display());
create_dir_all(&dirname)?;
}
}
let mut connect_opts = SqliteConnectOptions::from_str(path)?
.pragma("key", password.to_string())
.create_if_missing(true)
.journal_mode(SqliteJournalMode::Off);
connect_opts.log_statements(LevelFilter::Trace);
connect_opts.log_slow_statements(LevelFilter::Trace, Duration::from_micros(10));
let conn = SqlitePool::connect_with(connect_opts).await?;
info!("Opened connection at path {}", path);
Ok(Arc::new(WalletDb { conn }))
}
pub async fn init_db(&self) -> Result<()> {
info!("Initializing wallet database");
let tree = include_str!("../../../script/sql/tree.sql");
let keys = include_str!("../../../script/sql/keys.sql");
let coins = include_str!("../../../script/sql/coins.sql");
let mut conn = self.conn.acquire().await?;
debug!("Initializing merkle tree table");
sqlx::query(tree).execute(&mut conn).await?;
debug!("Initializing keys table");
sqlx::query(keys).execute(&mut conn).await?;
debug!("Initializing coins table");
sqlx::query(coins).execute(&mut conn).await?;
Ok(())
}
pub async fn key_gen(&self) -> Result<()> {
debug!("Attempting to generate keypairs");
let keypair = Keypair::random(&mut OsRng);
self.put_keypair(&keypair).await?;
Ok(())
}
pub async fn put_keypair(&self, keypair: &Keypair) -> Result<()> {
debug!("Writing keypair into the wallet database");
let pubkey = serialize(&keypair.public);
let secret = serialize(&keypair.secret);
let is_default = 0;
let mut conn = self.conn.acquire().await?;
sqlx::query("INSERT INTO keys(public, secret, is_default) VALUES (?1, ?2, ?3)")
.bind(pubkey)
.bind(secret)
.bind(is_default)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn set_default_keypair(&self, public: &PublicKey) -> Result<()> {
debug!("Set default keypair");
let mut conn = self.conn.acquire().await?;
let pubkey = serialize(public);
// unset previous default keypair
sqlx::query("UPDATE keys SET is_default = 0;").execute(&mut conn).await?;
// set new default keypair
sqlx::query("UPDATE keys SET is_default = 1 WHERE public = ?1;")
.bind(pubkey)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_default_keypair(&self) -> Result<Keypair> {
debug!("Returning default keypair");
let mut conn = self.conn.acquire().await?;
let is_default = 1;
let row = sqlx::query("SELECT * FROM keys WHERE is_default = ?1;")
.bind(is_default)
.fetch_one(&mut conn)
.await?;
let public: PublicKey = self.get_value_deserialized(row.get("public"))?;
let secret: SecretKey = self.get_value_deserialized(row.get("secret"))?;
Ok(Keypair { secret, public })
}
pub async fn get_keypairs(&self) -> Result<Vec<Keypair>> {
debug!("Returning keypairs");
let mut conn = self.conn.acquire().await?;
let mut keypairs = vec![];
for row in sqlx::query("SELECT * FROM keys").fetch_all(&mut conn).await? {
let public: PublicKey = self.get_value_deserialized(row.get("public"))?;
let secret: SecretKey = self.get_value_deserialized(row.get("secret"))?;
keypairs.push(Keypair { public, secret });
}
Ok(keypairs)
}
pub async fn tree_gen(&self) -> Result<BridgeTree<MerkleNode, 32>> {
debug!("Attempting to generate merkle tree");
let mut conn = self.conn.acquire().await?;
match sqlx::query("SELECT * FROM tree").fetch_one(&mut conn).await {
Ok(_) => {
error!("Tree already exists");
Err(Error::from(ClientFailed::TreeExists))
}
Err(_) => {
let tree = BridgeTree::<MerkleNode, 32>::new(100);
self.put_tree(&tree).await?;
Ok(tree)
}
}
}
pub async fn get_tree(&self) -> Result<BridgeTree<MerkleNode, 32>> {
debug!("Getting merkle tree");
let mut conn = self.conn.acquire().await?;
let row = sqlx::query("SELECT * FROM tree").fetch_one(&mut conn).await?;
let tree: BridgeTree<MerkleNode, 32> = bincode::deserialize(row.get("tree"))?;
Ok(tree)
}
pub async fn put_tree(&self, tree: &BridgeTree<MerkleNode, 32>) -> Result<()> {
debug!("Attempting to write merkle tree");
let mut conn = self.conn.acquire().await?;
let tree_bytes = bincode::serialize(tree)?;
debug!("Deleting old row");
sqlx::query("DELETE FROM tree;").execute(&mut conn).await?;
debug!("Inserting new tree");
sqlx::query("INSERT INTO tree (tree) VALUES (?1);")
.bind(tree_bytes)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_own_coins(&self) -> Result<OwnCoins> {
debug!("Finding own coins");
let is_spent = 0;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query("SELECT * FROM coins WHERE is_spent = ?1;")
.bind(is_spent)
.fetch_all(&mut conn)
.await?;
let mut own_coins = vec![];
for row in rows {
let coin = self.get_value_deserialized(row.get("coin"))?;
// Note
let serial = self.get_value_deserialized(row.get("serial"))?;
let coin_blind = self.get_value_deserialized(row.get("coin_blind"))?;
let value_blind = self.get_value_deserialized(row.get("valcom_blind"))?;
// TODO: FIXME:
let value_bytes: Vec<u8> = row.get("value");
let value = u64::from_le_bytes(value_bytes.try_into().unwrap());
let token_id = self.get_value_deserialized(row.get("token_id"))?;
let note = Note { serial, value, token_id, coin_blind, value_blind };
let secret = self.get_value_deserialized(row.get("secret"))?;
let nullifier = self.get_value_deserialized(row.get("nullifier"))?;
let oc = OwnCoin { coin, note, secret, nullifier };
own_coins.push(oc);
}
Ok(own_coins)
}
pub async fn put_own_coins(&self, own_coin: OwnCoin) -> Result<()> {
debug!("Putting own coin into wallet database");
let coin = self.get_value_serialized(&own_coin.coin.to_bytes())?;
let serial = self.get_value_serialized(&own_coin.note.serial)?;
let coin_blind = self.get_value_serialized(&own_coin.note.coin_blind)?;
let value_blind = self.get_value_serialized(&own_coin.note.value_blind)?;
let value = own_coin.note.value.to_le_bytes();
let token_id = self.get_value_serialized(&own_coin.note.token_id)?;
let secret = self.get_value_serialized(&own_coin.secret)?;
let is_spent = 0;
let nullifier = self.get_value_serialized(&own_coin.nullifier)?;
let mut conn = self.conn.acquire().await?;
sqlx::query(
"INSERT OR REPLACE INTO coins
(coin, serial, value, token_id, coin_blind,
valcom_blind, secret, is_spent, nullifier)
VALUES
(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);",
)
.bind(coin)
.bind(serial)
.bind(value.to_vec())
.bind(token_id)
.bind(coin_blind)
.bind(value_blind)
.bind(secret)
.bind(is_spent)
.bind(nullifier)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn remove_own_coins(&self) -> Result<()> {
debug!("Removing own coins from wallet database");
let mut conn = self.conn.acquire().await?;
sqlx::query("DROP TABLE coins;").execute(&mut conn).await?;
Ok(())
}
pub async fn confirm_spend_coin(&self, coin: &Coin) -> Result<()> {
debug!("Confirm spend coin");
let is_spent = 1;
let coin = self.get_value_serialized(coin)?;
let mut conn = self.conn.acquire().await?;
sqlx::query("UPDATE coins SET is_spent = ?1 WHERE coin = ?2;")
.bind(is_spent)
.bind(coin)
.execute(&mut conn)
.await?;
Ok(())
}
pub async fn get_balances(&self) -> Result<Balances> {
debug!("Getting tokens and balances");
let is_spent = 0;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query("SELECT value, token_id, nullifier FROM coins WHERE is_spent = ?1;")
.bind(is_spent)
.fetch_all(&mut conn)
.await?;
debug!("Found {} rows", rows.len());
let mut list = vec![];
for row in rows {
// TODO: FIXME:
let value_bytes: Vec<u8> = row.get("value");
let value = u64::from_le_bytes(value_bytes.try_into().unwrap());
let token_id = self.get_value_deserialized(row.get("token_id"))?;
let nullifier = self.get_value_deserialized(row.get("nullifier"))?;
list.push(Balance { token_id, value, nullifier });
}
Ok(Balances { list })
}
pub async fn get_token_id(&self) -> Result<Vec<DrkTokenId>> {
debug!("Getting token ID");
let is_spent = 0;
let mut conn = self.conn.acquire().await?;
let rows = sqlx::query("SELECT token_id FROM coins WHERE is_spent = ?1;")
.bind(is_spent)
.fetch_all(&mut conn)
.await?;
let mut token_ids = vec![];
for row in rows {
let token_id = self.get_value_deserialized(row.get("token_id"))?;
token_ids.push(token_id);
}
Ok(token_ids)
}
pub async fn token_id_exists(&self, token_id: DrkTokenId) -> Result<bool> {
debug!("Checking if token ID exists");
let is_spent = 0;
let id = self.get_value_serialized(&token_id)?;
let mut conn = self.conn.acquire().await?;
let id_check = sqlx::query("SELECT * FROM coins WHERE token_id = ?1 AND is_spent = ?2;")
.bind(id)
.bind(is_spent)
.fetch_optional(&mut conn)
.await?;
Ok(id_check.is_some())
}
pub async fn test_wallet(&self) -> Result<()> {
debug!("Testing wallet");
let mut conn = self.conn.acquire().await?;
let _row = sqlx::query("SELECT * FROM keys").fetch_one(&mut conn).await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::{
merkle_node::MerkleNode,
types::{DrkCoinBlind, DrkSerial, DrkValueBlind},
};
use group::ff::Field;
use incrementalmerkletree::{Frontier, Tree};
use pasta_curves::pallas;
use rand::rngs::OsRng;
const WPASS: &str = "darkfi";
fn dummy_coin(s: &SecretKey, v: u64, t: &DrkTokenId) -> OwnCoin {
let serial = DrkSerial::random(&mut OsRng);
let note = Note {
serial,
value: v,
token_id: *t,
coin_blind: DrkCoinBlind::random(&mut OsRng),
value_blind: DrkValueBlind::random(&mut OsRng),
};
let coin = Coin(pallas::Base::random(&mut OsRng));
let nullifier = Nullifier::new(*s, serial);
OwnCoin { coin, note, secret: *s, nullifier }
}
#[async_std::test]
async fn test_walletdb() -> Result<()> {
let wallet = WalletDb::new("sqlite::memory:", WPASS).await?;
let keypair = Keypair::random(&mut OsRng);
// init_db()
wallet.init_db().await?;
// tree_gen()
let mut tree1 = wallet.tree_gen().await?;
// put_keypair()
wallet.put_keypair(&keypair).await?;
let token_id = DrkTokenId::random(&mut OsRng);
let c0 = dummy_coin(&keypair.secret, 69, &token_id);
let c1 = dummy_coin(&keypair.secret, 420, &token_id);
let c2 = dummy_coin(&keypair.secret, 42, &token_id);
let c3 = dummy_coin(&keypair.secret, 11, &token_id);
// put_own_coins()
wallet.put_own_coins(c0).await?;
tree1.append(&MerkleNode::from_coin(&c0.coin));
tree1.witness();
wallet.put_own_coins(c1).await?;
tree1.append(&MerkleNode::from_coin(&c1.coin));
tree1.witness();
wallet.put_own_coins(c2).await?;
tree1.append(&MerkleNode::from_coin(&c2.coin));
tree1.witness();
wallet.put_own_coins(c3).await?;
tree1.append(&MerkleNode::from_coin(&c3.coin));
tree1.witness();
// We'll check this merkle root corresponds to the one we'll retrieve.
let root1 = tree1.root();
// put_tree()
wallet.put_tree(&tree1).await?;
// get_token_id()
let id = wallet.get_token_id().await?;
assert_eq!(id.len(), 4);
for i in id {
assert_eq!(i, token_id);
assert!(wallet.token_id_exists(i).await?);
}
// get_balances()
let balances = wallet.get_balances().await?;
assert_eq!(balances.list.len(), 4);
assert_eq!(balances.list[1].value, 420);
assert_eq!(balances.list[2].value, 42);
assert_eq!(balances.list[3].token_id, token_id);
/////////////////
//// keypair ////
/////////////////
let keypair2 = Keypair::random(&mut OsRng);
// add new keypair
wallet.put_keypair(&keypair2).await?;
// get all keypairs
let keypairs = wallet.get_keypairs().await?;
assert_eq!(keypair, keypairs[0]);
assert_eq!(keypair2, keypairs[1]);
// set the keypair at index 1 as the default keypair
wallet.set_default_keypair(&keypair2.public).await?;
// get default keypair
assert_eq!(keypair2, wallet.get_default_keypair().await?);
// get_own_coins()
let own_coins = wallet.get_own_coins().await?;
assert_eq!(own_coins.len(), 4);
assert_eq!(own_coins[0], c0);
assert_eq!(own_coins[1], c1);
assert_eq!(own_coins[2], c2);
assert_eq!(own_coins[3], c3);
// get_tree()
let tree2 = wallet.get_tree().await?;
let root2 = tree2.root();
assert_eq!(root1, root2);
// Let's try it once more to test sql replacing.
wallet.put_tree(&tree2).await?;
let tree3 = wallet.get_tree().await?;
let root3 = tree3.root();
assert_eq!(root2, root3);
Ok(())
}
}