|
@@ -0,0 +1,158 @@
|
|
|
|
|
+use drk::Drk;
|
|
|
|
|
+use url::Url;
|
|
|
|
|
+
|
|
|
|
|
+use crate::darkfi::Error;
|
|
|
|
|
+use darkfi::tx::Transaction;
|
|
|
|
|
+use darkfi_sdk::crypto::{Keypair, PublicKey, SecretKey};
|
|
|
|
|
+use std::sync::Arc;
|
|
|
|
|
+
|
|
|
|
|
+/// Darkfi wallet implementation.
|
|
|
|
|
+///
|
|
|
|
|
+/// Interacts with a running darkfid node.
|
|
|
|
|
+pub(crate) struct Wallet {
|
|
|
|
|
+ drk: Drk,
|
|
|
|
|
+
|
|
|
|
|
+ // user keypair; the default when the wallet is made
|
|
|
|
|
+ user_keypair: Keypair,
|
|
|
|
|
+
|
|
|
|
|
+ // swap keypair
|
|
|
|
|
+ swap_keypair: Option<Keypair>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl Wallet {
|
|
|
|
|
+ pub(crate) async fn new(
|
|
|
|
|
+ wallet_path: String,
|
|
|
|
|
+ wallet_pass: String,
|
|
|
|
|
+ endpoint: Url,
|
|
|
|
|
+ ex: Arc<smol::Executor<'static>>,
|
|
|
|
|
+ ) -> Result<Self, crate::Error> {
|
|
|
|
|
+ let drk = Drk::new(wallet_path, wallet_pass, endpoint, ex)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .map_err(|e| crate::Error::from(Error::DrkInitializationFailed(e)))?;
|
|
|
|
|
+ drk.initialize_wallet()
|
|
|
|
|
+ .await
|
|
|
|
|
+ .map_err(|e| crate::Error::from(Error::InitializeWallet(e)))?;
|
|
|
|
|
+ drk.initialize_money().await.map_err(|e| crate::Error::from(Error::InitializeMoney(e)))?;
|
|
|
|
|
+ drk.initialize_dao().await.map_err(|e| crate::Error::from(Error::InitializeDao(e)))?;
|
|
|
|
|
+
|
|
|
|
|
+ let user_keypair = Keypair::new(
|
|
|
|
|
+ drk.default_secret().await.map_err(|e| crate::Error::from(Error::DefaultSecret(e)))?,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ Ok(Self { drk, user_keypair, swap_keypair: None })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub(crate) async fn generate_swap_keypair(&mut self) -> Result<(), crate::Error> {
|
|
|
|
|
+ self.swap_keypair =
|
|
|
|
|
+ Some(self.drk.money_keygen().await.map_err(|e| crate::Error::from(Error::Keygen(e)))?);
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // get the user's DRK balance
|
|
|
|
|
+ pub(crate) async fn get_user_balance(&self) -> Result<u64, crate::Error> {
|
|
|
|
|
+ get_balance(&self.drk, self.user_keypair.secret).await
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // get the swap account's DRK balance
|
|
|
|
|
+ pub(crate) async fn get_swap_balance(&self) -> Result<u64, crate::Error> {
|
|
|
|
|
+ let swap_keypair =
|
|
|
|
|
+ self.swap_keypair.ok_or(crate::Error::from(Error::SwapKeypairDoesNotExist))?;
|
|
|
|
|
+
|
|
|
|
|
+ get_balance(&self.drk, swap_keypair.secret).await
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // build a transfer of DRK from the user account to the given recipient
|
|
|
|
|
+ pub(crate) async fn build_user_transfer(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ amount: u128,
|
|
|
|
|
+ recipient: PublicKey,
|
|
|
|
|
+ ) -> Result<Transaction, crate::Error> {
|
|
|
|
|
+ let token_id =
|
|
|
|
|
+ self.drk.get_token("DRK".to_string()).await.expect("token id must exist for DRK");
|
|
|
|
|
+ self.drk
|
|
|
|
|
+ .transfer_with_signer(&amount.to_string(), token_id, recipient, self.user_keypair)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .map_err(|e| crate::Error::from(Error::BuildTransfer(e)))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // build a transfer of DRK from the swap account to the given recipient
|
|
|
|
|
+ pub(crate) async fn build_swap_transfer(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ amount: u128,
|
|
|
|
|
+ recipient: PublicKey,
|
|
|
|
|
+ ) -> Result<Transaction, crate::Error> {
|
|
|
|
|
+ let swap_keypair =
|
|
|
|
|
+ self.swap_keypair.ok_or(crate::Error::from(Error::SwapKeypairDoesNotExist))?;
|
|
|
|
|
+ let token_id =
|
|
|
|
|
+ self.drk.get_token("DRK".to_string()).await.expect("token id must exist for DRK");
|
|
|
|
|
+ self.drk
|
|
|
|
|
+ .transfer_with_signer(&amount.to_string(), token_id, recipient, swap_keypair)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .map_err(|e| crate::Error::from(Error::BuildTransfer(e)))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // submit a transaction to the network
|
|
|
|
|
+ pub(crate) async fn submit_transaction(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ tx: &Transaction,
|
|
|
|
|
+ ) -> Result<String, crate::Error> {
|
|
|
|
|
+ self.drk.broadcast_tx(tx).await.map_err(|e| crate::Error::from(Error::SubmitTransaction(e)))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async fn get_balance(drk: &Drk, secret: SecretKey) -> Result<u64, crate::Error> {
|
|
|
|
|
+ use std::collections::HashMap;
|
|
|
|
|
+
|
|
|
|
|
+ let mut coins =
|
|
|
|
|
+ drk.get_coins(false).await.map_err(|e| crate::Error::from(Error::MoneyBalance(e)))?;
|
|
|
|
|
+ coins.retain(|x| x.0.note.spend_hook == darkfi_sdk::crypto::FuncId::none());
|
|
|
|
|
+ coins.retain(|x| x.0.secret == secret);
|
|
|
|
|
+
|
|
|
|
|
+ let mut balmap: HashMap<String, u64> = HashMap::new();
|
|
|
|
|
+
|
|
|
|
|
+ for coin in coins {
|
|
|
|
|
+ let mut value = coin.0.note.value;
|
|
|
|
|
+
|
|
|
|
|
+ if let Some(prev) = balmap.get(&coin.0.note.token_id.to_string()) {
|
|
|
|
|
+ value += prev;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ balmap.insert(coin.0.note.token_id.to_string(), value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let aliases_map = drk
|
|
|
|
|
+ .get_aliases_mapped_by_token()
|
|
|
|
|
+ .await
|
|
|
|
|
+ .map_err(|e| crate::Error::from(Error::GetAliasesMappedByToken(e)))?;
|
|
|
|
|
+ for (token_id, balance) in balmap.iter() {
|
|
|
|
|
+ let alias = match aliases_map.get(token_id) {
|
|
|
|
|
+ Some(a) => a,
|
|
|
|
|
+ None => "-", // TODO: should every token have an alias?
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if alias == "DRK" {
|
|
|
|
|
+ return Ok(*balance);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return Ok(0);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[cfg(test)]
|
|
|
|
|
+mod test {
|
|
|
|
|
+ use super::*;
|
|
|
|
|
+ use std::str::FromStr;
|
|
|
|
|
+
|
|
|
|
|
+ #[ignore = "requires a running darkfid node"]
|
|
|
|
|
+ #[async_std::test]
|
|
|
|
|
+ async fn test_get_balance() {
|
|
|
|
|
+ let drk = Wallet::new(
|
|
|
|
|
+ "~/darkfi/contrib/localnet/darkfid-single-node/drk/wallet.db".to_string(),
|
|
|
|
|
+ "testing".to_string(),
|
|
|
|
|
+ Url::from_str("tcp://127.0.0.1:48340").unwrap(),
|
|
|
|
|
+ Arc::new(smol::Executor::new()),
|
|
|
|
|
+ )
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|