Cesar Rodas 1 년 전
부모
커밋
d95a2826a4
8개의 변경된 파일67개의 추가작업 그리고 24개의 파일을 삭제
  1. 0 1
      Cargo.lock
  2. 0 5
      Cargo.toml
  3. 35 2
      src/main.rs
  4. 16 12
      utxo/src/amount.rs
  5. 7 0
      utxo/src/asset_manager.rs
  6. 4 0
      utxo/src/ledger.rs
  7. 1 1
      utxo/src/lib.rs
  8. 4 3
      utxo/src/sqlite/mod.rs

+ 0 - 1
Cargo.lock

@@ -1523,7 +1523,6 @@ dependencies = [
  "ledger-utxo",
  "serde",
  "serde_json",
- "sqlx",
  "tokio 1.32.0",
 ]
 

+ 0 - 5
Cargo.toml

@@ -9,11 +9,6 @@ members = ["utxo"]
 
 [dependencies]
 ledger-utxo = { path = "utxo", features = ["sqlite"] }
-sqlx = { version = "0.7.1", features = [
-    "sqlite",
-    "runtime-tokio",
-    "runtime-async-std-native-tls",
-] }
 actix-web = "3"
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"

+ 35 - 2
src/main.rs

@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use actix_web::{
     error::InternalError, get, middleware::Logger, post, web, App, HttpResponse, HttpServer,
     Responder,
@@ -5,7 +7,6 @@ use actix_web::{
 use ledger_utxo::{AccountId, AssetDefinition, AssetManager, Status, TransactionId};
 use serde::{Deserialize, Serialize};
 use serde_json::json;
-use sqlx::sqlite::SqlitePoolOptions;
 
 #[derive(Deserialize)]
 pub struct Movement {
@@ -63,6 +64,37 @@ struct Item {
     name: String,
 }
 
+#[derive(Serialize)]
+struct AccountResponse {
+    amount: String,
+    asset: Arc<str>,
+}
+
+#[get("/balance/{id}")]
+async fn get_balance(info: web::Path<AccountId>, ledger: web::Data<Ledger>) -> impl Responder {
+    let asset_manager = ledger._inner.asset_manager();
+    match ledger._inner.get_balance(&info.0).await {
+        Ok(balances) => {
+            match balances
+                .into_iter()
+                .map(|amount| {
+                    asset_manager
+                        .get_name(amount.asset().id)
+                        .map(|asset| AccountResponse {
+                            amount: amount.to_string(),
+                            asset: asset,
+                        })
+                })
+                .collect::<Result<Vec<_>, _>>()
+            {
+                Ok(balances) => HttpResponse::Ok().json(balances),
+                Err(e) => HttpResponse::BadRequest().json(e),
+            }
+        }
+        Err(e) => HttpResponse::BadRequest().json(e),
+    }
+}
+
 #[get("/tx/{id}")]
 async fn get_transaction(
     info: web::Path<TransactionId>,
@@ -125,7 +157,7 @@ async fn main() -> std::io::Result<()> {
         AssetDefinition::new(2, "USD", 4),
     ]);
 
-    let pool = SqlitePoolOptions::new()
+    let pool = ledger_utxo::sqlite::SqlitePoolOptions::new()
         .connect("sqlite:./test.db")
         .await
         .expect("pool");
@@ -148,6 +180,7 @@ async fn main() -> std::io::Result<()> {
                 )
                 .into()
             }))
+            .service(get_balance)
             .service(get_transaction)
             .service(deposit)
             .service(create_transaction)

+ 16 - 12
utxo/src/amount.rs

@@ -124,6 +124,9 @@ impl ToString for Amount {
             left,
             right
         )
+        .trim_end_matches("0")
+        .trim_end_matches(".")
+        .to_owned()
     }
 }
 
@@ -138,7 +141,7 @@ mod test {
             precision: 4,
         };
         let amount = usd.new_amount(1022100);
-        assert_eq!(amount.to_string(), "102.2100");
+        assert_eq!(amount.to_string(), "102.21");
     }
 
     #[test]
@@ -147,17 +150,18 @@ mod test {
             id: 1,
             precision: 8,
         };
-        assert_eq!(btc.new_amount(1022100).to_string(), "0.01022100");
-        assert_eq!(btc.new_amount(10).to_string(), "0.00000010");
-        assert_eq!(btc.new_amount(10000000).to_string(), "0.10000000");
-        assert_eq!(btc.new_amount(100000000).to_string(), "1.00000000");
+        assert_eq!(btc.new_amount(1022100).to_string(), "0.010221");
+        assert_eq!(btc.new_amount(10).to_string(), "0.0000001");
+        assert_eq!(btc.new_amount(10000000).to_string(), "0.1");
+        assert_eq!(btc.new_amount(100000000).to_string(), "1");
         assert_eq!(
             btc.new_amount(100000000)
                 .checked_add(&btc.new_amount(100000000))
                 .unwrap()
                 .to_string(),
-            "2.00000000"
+            "2",
         );
+        assert_eq!(btc.new_amount(1000000000).to_string(), "10");
     }
 
     #[test]
@@ -167,16 +171,16 @@ mod test {
             precision: 8,
         };
         let parsed_amount = Amount::from_human(btc, "0.1").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "0.10000000");
+        assert_eq!(parsed_amount.to_string(), "0.1");
         let parsed_amount = Amount::from_human(btc, "-0.1").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "-0.10000000");
+        assert_eq!(parsed_amount.to_string(), "-0.1");
         let parsed_amount = Amount::from_human(btc, "-0.000001").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "-0.00000100");
+        assert_eq!(parsed_amount.to_string(), "-0.000001");
         let parsed_amount = Amount::from_human(btc, "-0.000000001").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "0.00000000");
+        assert_eq!(parsed_amount.to_string(), "0");
         let parsed_amount = Amount::from_human(btc, "0.000001").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "0.00000100");
+        assert_eq!(parsed_amount.to_string(), "0.000001");
         let parsed_amount = Amount::from_human(btc, "-0.000000100001").expect("valid amount");
-        assert_eq!(parsed_amount.to_string(), "-0.00000010");
+        assert_eq!(parsed_amount.to_string(), "-0.0000001");
     }
 }

+ 7 - 0
utxo/src/asset_manager.rs

@@ -32,6 +32,13 @@ impl AssetManager {
         }
     }
 
+    pub fn get_name(&self, id: AssetId) -> Result<Arc<str>, Error> {
+        self.assets
+            .get(&id)
+            .map(|asset| asset.name.clone())
+            .ok_or(Error::AssetIdNotFound(id))
+    }
+
     pub fn asset(&self, id: AssetId) -> Result<Asset, Error> {
         self.assets
             .get(&id)

+ 4 - 0
utxo/src/ledger.rs

@@ -33,6 +33,10 @@ where
         Ok(self.asset_manager.human_amount_by_name(asset, amount)?)
     }
 
+    pub fn asset_manager(&self) -> &AssetManager {
+        &self.asset_manager
+    }
+
     /// Selects the unspent payments to be used as inputs of the new transaction.
     ///
     /// This function also returns a list of transactions that will be used as

+ 1 - 1
utxo/src/lib.rs

@@ -6,7 +6,7 @@ mod id;
 mod ledger;
 mod payment;
 #[cfg(any(feature = "sqlite", test))]
-mod sqlite;
+pub mod sqlite;
 mod status;
 pub mod storage;
 #[cfg(test)]

+ 4 - 3
utxo/src/sqlite/mod.rs

@@ -13,6 +13,7 @@ use std::{collections::HashMap, marker::PhantomData};
 mod batch;
 
 pub use batch::Batch;
+pub use sqlx::sqlite::SqlitePoolOptions;
 
 pub struct Sqlite<'a> {
     db: sqlx::SqlitePool,
@@ -44,7 +45,7 @@ impl<'a> Sqlite<'a> {
                 "updated_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
                 PRIMARY KEY ("transaction_id", "position_id")
             );
-            CREATE INDEX IF NOT EXISTS payments_to ON payments ("to", "asset_id", "status", "spent_by");
+            CREATE INDEX IF NOT EXISTS payments_to ON payments ("to", "asset_id", "spent_by", "status");
             CREATE TABLE IF NOT EXISTS "transactions" (
                 "transaction_id" VARCHAR(66) NOT NULL,
                 "status" INTEGER NOT NULL,
@@ -62,7 +63,7 @@ impl<'a> Sqlite<'a> {
                 "updated_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
                 PRIMARY KEY("account_id", "transaction_id")
             );
-            CREATE INDEX "type" ON "transaction_accounts" ("account_id", "type", "created_at");
+            CREATE INDEX IF NOT EXISTS "type" ON "transaction_accounts" ("account_id", "type", "created_at");
             CREATE TABLE IF NOT EXISTS "transaction_input_payments" (
                 "transaction_id" VARCHAR(66) NOT NULL,
                 "payment_transaction_id" VARCHAR(66) NOT NULL,
@@ -194,7 +195,7 @@ impl<'a> Storage<'a, Batch<'a>> for Sqlite<'a> {
             FROM
                 "payments"
             WHERE
-                "to" = ? AND status = ? AND "spent_by" IS NULL
+                "to" = ? AND "spent_by" IS NULL AND status = ? 
             "#,
         )
         .bind(account.to_string())