Browse Source

feat(mint): issued and redeamed by keyset

thesimplekid 8 months ago
parent
commit
ba19663531

+ 26 - 2
crates/cdk-redb/src/mint/mod.rs

@@ -572,6 +572,30 @@ impl MintDatabase for MintRedbDatabase {
         Ok(states)
     }
 
+    async fn get_proofs_by_keyset_id(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
+        let db = self.db.lock().await;
+        let read_txn = db.begin_read().map_err(Error::from)?;
+        let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
+
+        let proofs_for_id: Proofs = table
+            .iter()
+            .map_err(Error::from)?
+            .flatten()
+            .flat_map(|(_, p)| serde_json::from_str::<Proof>(p.value()))
+            .filter(|p| &p.keyset_id == keyset_id)
+            .collect();
+
+        let proof_ys: Vec<PublicKey> = proofs_for_id.iter().flat_map(|p| p.y()).collect();
+        assert_eq!(proofs_for_id.len(), proof_ys.len());
+
+        let states = self.get_proofs_states(&proof_ys).await?;
+
+        Ok((proofs_for_id, states))
+    }
+
     async fn update_proofs_states(
         &self,
         ys: &[PublicKey],
@@ -647,7 +671,7 @@ impl MintDatabase for MintRedbDatabase {
         Ok(())
     }
 
-    async fn get_blinded_signatures(
+    async fn get_blind_signatures(
         &self,
         blinded_messages: &[PublicKey],
     ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
@@ -671,7 +695,7 @@ impl MintDatabase for MintRedbDatabase {
         Ok(signatures)
     }
 
-    async fn get_blinded_signatures_for_keyset(
+    async fn get_blind_signatures_for_keyset(
         &self,
         keyset_id: &Id,
     ) -> Result<Vec<BlindSignature>, Self::Err> {

+ 55 - 2
crates/cdk-sqlite/src/mint/mod.rs

@@ -573,6 +573,35 @@ WHERE y=?;
         Ok(states)
     }
 
+    async fn get_proofs_by_keyset_id(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
+        let rec = sqlx::query(
+            r#"
+SELECT *
+FROM proof
+WHERE keyset_id=?;
+        "#,
+        )
+        .bind(keyset_id.to_string())
+        .fetch_all(&self.pool)
+        .await
+        .map_err(Error::from)?;
+
+        let mut proofs_for_id = vec![];
+        let mut states = vec![];
+
+        for row in rec {
+            let (proof, state) = sqlite_row_to_proof_with_state(row)?;
+
+            proofs_for_id.push(proof);
+            states.push(state);
+        }
+
+        Ok((proofs_for_id, states))
+    }
+
     async fn update_proofs_states(
         &self,
         ys: &[PublicKey],
@@ -657,7 +686,7 @@ VALUES (?, ?, ?, ?);
 
         Ok(())
     }
-    async fn get_blinded_signatures(
+    async fn get_blind_signatures(
         &self,
         blinded_messages: &[PublicKey],
     ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
@@ -686,7 +715,7 @@ WHERE y=?;
         Ok(signatures)
     }
 
-    async fn get_blinded_signatures_for_keyset(
+    async fn get_blind_signatures_for_keyset(
         &self,
         keyset_id: &Id,
     ) -> Result<Vec<BlindSignature>, Self::Err> {
@@ -811,6 +840,30 @@ fn sqlite_row_to_proof(row: SqliteRow) -> Result<Proof, Error> {
     })
 }
 
+fn sqlite_row_to_proof_with_state(row: SqliteRow) -> Result<(Proof, Option<State>), Error> {
+    let row_amount: i64 = row.try_get("amount").map_err(Error::from)?;
+    let keyset_id: String = row.try_get("keyset_id").map_err(Error::from)?;
+    let row_secret: String = row.try_get("secret").map_err(Error::from)?;
+    let row_c: Vec<u8> = row.try_get("c").map_err(Error::from)?;
+    let row_witness: Option<String> = row.try_get("witness").map_err(Error::from)?;
+
+    let row_state: Option<String> = row.try_get("state").map_err(Error::from)?;
+
+    let state = row_state.and_then(|s| State::from_str(&s).ok());
+
+    Ok((
+        Proof {
+            amount: Amount::from(row_amount as u64),
+            keyset_id: Id::from_str(&keyset_id)?,
+            secret: Secret::from_str(&row_secret)?,
+            c: PublicKey::from_slice(&row_c)?,
+            witness: row_witness.and_then(|w| serde_json::from_str(&w).ok()),
+            dleq: None,
+        },
+        state,
+    ))
+}
+
 fn sqlite_row_to_blind_signature(row: SqliteRow) -> Result<BlindSignature, Error> {
     let row_amount: i64 = row.try_get("amount").map_err(Error::from)?;
     let keyset_id: String = row.try_get("keyset_id").map_err(Error::from)?;

+ 25 - 2
crates/cdk/src/cdk_database/mint_memory.rs

@@ -273,6 +273,29 @@ impl MintDatabase for MintMemoryDatabase {
         Ok(states)
     }
 
+    async fn get_proofs_by_keyset_id(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err> {
+        let proofs = self.proofs.read().await;
+
+        let proofs_for_id: Proofs = proofs
+            .iter()
+            .filter_map(|(_, p)| match &p.keyset_id == keyset_id {
+                true => Some(p),
+                false => None,
+            })
+            .cloned()
+            .collect();
+
+        let proof_ys: Vec<PublicKey> = proofs_for_id.iter().flat_map(|p| p.y()).collect();
+        assert_eq!(proofs_for_id.len(), proof_ys.len());
+
+        let states = self.get_proofs_states(&proof_ys).await?;
+
+        Ok((proofs_for_id, states))
+    }
+
     async fn add_blind_signatures(
         &self,
         blinded_message: &[PublicKey],
@@ -287,7 +310,7 @@ impl MintDatabase for MintMemoryDatabase {
         Ok(())
     }
 
-    async fn get_blinded_signatures(
+    async fn get_blind_signatures(
         &self,
         blinded_messages: &[PublicKey],
     ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
@@ -304,7 +327,7 @@ impl MintDatabase for MintMemoryDatabase {
         Ok(signatures)
     }
 
-    async fn get_blinded_signatures_for_keyset(
+    async fn get_blind_signatures_for_keyset(
         &self,
         keyset_id: &Id,
     ) -> Result<Vec<BlindSignature>, Self::Err> {

+ 7 - 2
crates/cdk/src/cdk_database/mod.rs

@@ -225,6 +225,11 @@ pub trait MintDatabase {
         ys: &[PublicKey],
         proofs_state: State,
     ) -> Result<Vec<Option<State>>, Self::Err>;
+    /// Get [`Proofs`] by state
+    async fn get_proofs_by_keyset_id(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
 
     /// Add [`BlindSignature`]
     async fn add_blind_signatures(
@@ -233,12 +238,12 @@ pub trait MintDatabase {
         blind_signatures: &[BlindSignature],
     ) -> Result<(), Self::Err>;
     /// Get [`BlindSignature`]s
-    async fn get_blinded_signatures(
+    async fn get_blind_signatures(
         &self,
         blinded_messages: &[PublicKey],
     ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
     /// Get [`BlindSignature`]s for keyset_id
-    async fn get_blinded_signatures_for_keyset(
+    async fn get_blind_signatures_for_keyset(
         &self,
         keyset_id: &Id,
     ) -> Result<Vec<BlindSignature>, Self::Err>;

+ 50 - 4
crates/cdk/src/mint/mod.rs

@@ -489,7 +489,7 @@ impl Mint {
 
         if self
             .localstore
-            .get_blinded_signatures(&blinded_messages)
+            .get_blind_signatures(&blinded_messages)
             .await?
             .iter()
             .flatten()
@@ -600,7 +600,7 @@ impl Mint {
 
         if self
             .localstore
-            .get_blinded_signatures(&blinded_messages)
+            .get_blind_signatures(&blinded_messages)
             .await?
             .iter()
             .flatten()
@@ -984,7 +984,7 @@ impl Mint {
 
             if self
                 .localstore
-                .get_blinded_signatures(&blinded_messages)
+                .get_blind_signatures(&blinded_messages)
                 .await?
                 .iter()
                 .flatten()
@@ -1083,7 +1083,7 @@ impl Mint {
 
         let blinded_signatures = self
             .localstore
-            .get_blinded_signatures(&blinded_message)
+            .get_blind_signatures(&blinded_message)
             .await?;
 
         assert_eq!(blinded_signatures.len(), output_len);
@@ -1128,6 +1128,52 @@ impl Mint {
     pub fn generate_keyset(&self, keyset_info: MintKeySetInfo) -> MintKeySet {
         MintKeySet::generate_from_xpriv(&self.secp_ctx, self.xpriv, keyset_info)
     }
+
+    /// Get the total amount issed by keyset
+    #[instrument(skip_all)]
+    pub async fn total_issued(&self) -> Result<HashMap<Id, Amount>, Error> {
+        let keysets = self.localstore.get_keyset_infos().await?;
+
+        let mut total_issued = HashMap::new();
+
+        for keyset in keysets {
+            let blinded = self
+                .localstore
+                .get_blind_signatures_for_keyset(&keyset.id)
+                .await?;
+
+            let total = blinded.iter().map(|b| b.amount).sum();
+
+            total_issued.insert(keyset.id, total);
+        }
+
+        Ok(total_issued)
+    }
+
+    /// Total redeamed for keyset
+    #[instrument(skip_all)]
+    pub async fn total_redeamed(&self) -> Result<HashMap<Id, Amount>, Error> {
+        let keysets = self.localstore.get_keyset_infos().await?;
+
+        let mut total_redeamed = HashMap::new();
+
+        for keyset in keysets {
+            let (proofs, state) = self.localstore.get_proofs_by_keyset_id(&keyset.id).await?;
+
+            let total_spent = proofs
+                .iter()
+                .zip(state)
+                .filter_map(|(p, s)| match s == Some(State::Spent) {
+                    true => Some(p.amount),
+                    false => None,
+                })
+                .sum();
+
+            total_redeamed.insert(keyset.id, total_spent);
+        }
+
+        Ok(total_redeamed)
+    }
 }
 
 /// Mint Fee Reserve