Skip to content

Commit

Permalink
feat: store and get melt requests
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Sep 23, 2024
1 parent cac0f31 commit 2e80677
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 30 deletions.
19 changes: 1 addition & 18 deletions crates/cdk-axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use axum::Router;
use cdk::cdk_lightning::{self, MintLightning};
use cdk::mint::Mint;
use cdk::mint_url::MintUrl;
use cdk::nuts::{CurrencyUnit, PaymentMethod};
use cdk::types::LnKey;
use router_handlers::*;

mod router_handlers;
Expand Down Expand Up @@ -66,20 +66,3 @@ pub struct MintState {
mint_url: MintUrl,
quote_ttl: u64,
}

/// Key used in hashmap of ln backends to identify what unit and payment method
/// it is for
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct LnKey {
/// Unit of Payment backend
pub unit: CurrencyUnit,
/// Method of payment backend
pub method: PaymentMethod,
}

impl LnKey {
/// Create new [`LnKey`]
pub fn new(unit: CurrencyUnit, method: PaymentMethod) -> Self {
Self { unit, method }
}
}
2 changes: 1 addition & 1 deletion crates/cdk-integration-tests/src/init_regtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use cdk::{
cdk_lightning::MintLightning,
mint::{FeeReserve, Mint},
nuts::{CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings},
types::LnKey,
};
use cdk_axum::LnKey;
use cdk_cln::Cln as CdkCln;
use futures::StreamExt;
use ln_regtest_rs::{
Expand Down
2 changes: 1 addition & 1 deletion crates/cdk-integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ use cdk::nuts::{
CurrencyUnit, Id, KeySet, MeltMethodSettings, MintInfo, MintMethodSettings, MintQuoteState,
Nuts, PaymentMethod, PreMintSecrets, Proofs,
};
use cdk::types::LnKey;
use cdk::wallet::client::HttpClient;
use cdk::{Mint, Wallet};
use cdk_axum::LnKey;
use cdk_fake_wallet::FakeWallet;
use futures::StreamExt;
use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
Expand Down
40 changes: 37 additions & 3 deletions crates/cdk-mintd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use cdk::nuts::{
nut04, nut05, ContactInfo, CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings,
MintVersion, MppMethodSettings, Nuts, PaymentMethod,
};
use cdk_axum::LnKey;
use cdk::types::LnKey;
use cdk_cln::Cln;
use cdk_fake_wallet::FakeWallet;
use cdk_lnbits::LNbits;
Expand Down Expand Up @@ -438,9 +438,17 @@ async fn main() -> anyhow::Result<()> {
// it is possible that a mint quote was paid but the mint has not been updated
// this will check and update the mint state of those quotes
for ln in ln_backends.values() {
check_pending_quotes(Arc::clone(&mint), Arc::clone(ln)).await?;
check_pending_mint_quotes(Arc::clone(&mint), Arc::clone(ln)).await?;
}

/*
check_pending_melt_quotes(
Arc::clone(&mint),
ln_backends.values().into_iter().collect(),
)
.await;
*/

let mint_url = settings.info.url;
let listen_addr = settings.info.listen_host;
let listen_port = settings.info.listen_port;
Expand Down Expand Up @@ -505,7 +513,7 @@ async fn handle_paid_invoice(mint: Arc<Mint>, request_lookup_id: &str) -> Result
}

/// Used on mint start up to check status of all pending mint quotes
async fn check_pending_quotes(
async fn check_pending_mint_quotes(
mint: Arc<Mint>,
ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>,
) -> Result<()> {
Expand Down Expand Up @@ -539,6 +547,32 @@ async fn check_pending_quotes(
Ok(())
}

/*
async fn check_pending_melt_quotes(
mint: Arc<Mint>,
ln_backends: Vec<Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
) -> Result<()> {
let melt_quotes = mint.localstore.get_melt_quotes().await?;
let pending_quotes: Vec<MeltQuote> = melt_quotes
.into_iter()
.filter(|q| q.state == MeltQuoteState::Pending || q.state == MeltQuoteState::Unknown)
.collect();
for pending_quote in pending_quotes {
let mut payment_response = vec![];
for ln_backend in ln_backends.iter() {
let pay_invoice_response = ln_backend
.check_outgoing_payment(&pending_quote.request_lookup_id)
.await?;
payment_response.push(pay_invoice_response);
}
}
todo!()
}
*/

fn expand_path(path: &str) -> Option<PathBuf> {
if path.starts_with('~') {
if let Some(home_dir) = home::home_dir().as_mut() {
Expand Down
48 changes: 46 additions & 2 deletions crates/cdk-redb/src/mint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use cdk::cdk_database::MintDatabase;
use cdk::dhke::hash_to_curve;
use cdk::mint::{MintKeySetInfo, MintQuote};
use cdk::nuts::{
BlindSignature, CurrencyUnit, Id, MeltQuoteState, MintQuoteState, Proof, Proofs, PublicKey,
State,
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
Proofs, PublicKey, State,
};
use cdk::types::LnKey;
use cdk::{cdk_database, mint};
use migrations::migrate_01_to_02;
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
Expand All @@ -39,6 +40,8 @@ const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<&str, &str> =
MultimapTableDefinition::new("quote_signatures");

const MELT_REQUESTS: TableDefinition<&str, (&str, &str)> = TableDefinition::new("melt_requests");

const DATABASE_VERSION: u32 = 4;

/// Mint Redbdatabase
Expand Down Expand Up @@ -735,4 +738,45 @@ impl MintDatabase for MintRedbDatabase {
})
.collect())
}

/// Add melt request
async fn add_melt_request(
&self,
melt_request: MeltBolt11Request,
ln_key: LnKey,
) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?;
let mut table = write_txn.open_table(MELT_REQUESTS).map_err(Error::from)?;

table
.insert(
melt_request.quote.as_str(),
(
serde_json::to_string(&melt_request)?.as_str(),
serde_json::to_string(&ln_key)?.as_str(),
),
)
.map_err(Error::from)?;

Ok(())
}
/// Get melt request
async fn get_melt_request(
&self,
quote_id: &str,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn.open_table(MELT_REQUESTS).map_err(Error::from)?;

match table.get(quote_id).map_err(Error::from)? {
Some(melt_request) => {
let (melt_request_str, ln_key_str) = melt_request.value();
let melt_request = serde_json::from_str(melt_request_str)?;
let ln_key = serde_json::from_str(ln_key_str)?;

Ok(Some((melt_request, ln_key)))
}
None => Ok(None),
}
}
}
3 changes: 3 additions & 0 deletions crates/cdk-sqlite/src/mint/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub enum Error {
/// Invalid Database Path
#[error("Invalid database path")]
InvalidDbPath,
/// Serde Error
#[error(transparent)]
Serde(#[from] serde_json::Error),
}

impl From<Error> for cdk::cdk_database::Error {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Melt Requesr Table
CREATE TABLE IF NOT EXISTS melt_request (
id TEXT PRIMARY KEY,
inputs TEXT NOT NULL,
outputs TEXT,
method TEXT NOT NULL,
unit TEXT NOT NULL,
);
106 changes: 104 additions & 2 deletions crates/cdk-sqlite/src/mint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ use cdk::mint::{MintKeySetInfo, MintQuote};
use cdk::mint_url::MintUrl;
use cdk::nuts::nut05::QuoteState;
use cdk::nuts::{
BlindSignature, CurrencyUnit, Id, MeltQuoteState, MintQuoteState, Proof, Proofs, PublicKey,
State,
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState,
PaymentMethod, Proof, Proofs, PublicKey, State,
};
use cdk::secret::Secret;
use cdk::types::LnKey;
use cdk::{mint, Amount};
use error::Error;
use lightning_invoice::Bolt11Invoice;
Expand Down Expand Up @@ -1121,6 +1122,86 @@ WHERE keyset_id=?;
}
}
}

async fn add_melt_request(
&self,
melt_request: MeltBolt11Request,
ln_key: LnKey,
) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;

let res = sqlx::query(
r#"
INSERT OR REPLACE INTO melt_request
(id, inputs, outputs, method, unit)
VALUES (?, ?, ?, ?, ?);
"#,
)
.bind(melt_request.quote)
.bind(serde_json::to_string(&melt_request.inputs)?)
.bind(serde_json::to_string(&melt_request.outputs)?)
.bind(ln_key.method.to_string())
.bind(ln_key.unit.to_string())
.execute(&mut transaction)
.await;

match res {
Ok(_) => {
transaction.commit().await.map_err(Error::from)?;
Ok(())
}
Err(err) => {
tracing::error!("SQLite Could not update keyset");
if let Err(err) = transaction.rollback().await {
tracing::error!("Could not rollback sql transaction: {}", err);
}

Err(Error::from(err).into())
}
}
}

async fn get_melt_request(
&self,
quote_id: &str,
) -> Result<Option<(MeltBolt11Request, LnKey)>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;

let rec = sqlx::query(
r#"
SELECT *
FROM melt_request
WHERE id=?;
"#,
)
.bind(quote_id)
.fetch_one(&mut transaction)
.await;

match rec {
Ok(rec) => {
transaction.commit().await.map_err(Error::from)?;

let (request, key) = sqlite_row_to_melt_request(rec)?;

Ok(Some((request, key)))
}
Err(err) => match err {
sqlx::Error::RowNotFound => {
transaction.commit().await.map_err(Error::from)?;
return Ok(None);
}
_ => {
return {
if let Err(err) = transaction.rollback().await {
tracing::error!("Could not rollback sql transaction: {}", err);
}
Err(Error::SQLX(err).into())
}
}
},
}
}
}

fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {
Expand Down Expand Up @@ -1259,3 +1340,24 @@ fn sqlite_row_to_blind_signature(row: SqliteRow) -> Result<BlindSignature, Error
dleq: None,
})
}

fn sqlite_row_to_melt_request(row: SqliteRow) -> Result<(MeltBolt11Request, LnKey), Error> {
let quote_id: String = row.try_get("id").map_err(Error::from)?;
let row_inputs: String = row.try_get("inputs").map_err(Error::from)?;
let row_outputs: Option<String> = row.try_get("outputs").map_err(Error::from)?;
let row_method: String = row.try_get("method").map_err(Error::from)?;
let row_unit: String = row.try_get("unit").map_err(Error::from)?;

let melt_request = MeltBolt11Request {
quote: quote_id,
inputs: serde_json::from_str(&row_inputs)?,
outputs: row_outputs.and_then(|o| serde_json::from_str(&o).ok()),
};

let ln_key = LnKey {
unit: CurrencyUnit::from_str(&row_unit)?,
method: PaymentMethod::from_str(&row_method)?,
};

Ok((melt_request, ln_key))
}
Loading

0 comments on commit 2e80677

Please sign in to comment.