mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-09 15:16:00 +01:00
feat: nostr receive
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -30,6 +30,7 @@ jobs:
|
||||
-p cdk --no-default-features,
|
||||
-p cdk --no-default-features --features wallet,
|
||||
-p cdk --no-default-features --features mint,
|
||||
-p cdk --no-default-features --features wallet --features nostr,
|
||||
-p cdk-redb
|
||||
]
|
||||
steps:
|
||||
@@ -64,6 +65,7 @@ jobs:
|
||||
-p cdk,
|
||||
-p cdk --no-default-features,
|
||||
-p cdk --no-default-features --features wallet,
|
||||
-p cdk --no-default-features --features wallet --features nostr,
|
||||
-p cdk-js
|
||||
]
|
||||
steps:
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
[language-server.rust-analyzer.config]
|
||||
cargo = { features = ["wallet", "mint"] }
|
||||
cargo = { features = ["wallet", "mint", "nostr"] }
|
||||
|
||||
@@ -12,6 +12,7 @@ rust-version.workspace = true
|
||||
default = ["mint", "wallet"]
|
||||
mint = ["cdk/mint"]
|
||||
wallet = ["cdk/wallet"]
|
||||
nostr = ["cdk/nostr"]
|
||||
|
||||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
|
||||
@@ -5,6 +5,8 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use cdk::cdk_database;
|
||||
use cdk::cdk_database::WalletDatabase;
|
||||
#[cfg(feature = "nostr")]
|
||||
use cdk::nuts::PublicKey;
|
||||
use cdk::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs};
|
||||
use cdk::types::{MeltQuote, MintQuote};
|
||||
use cdk::url::UncheckedUrl;
|
||||
@@ -25,6 +27,8 @@ const PENDING_PROOFS_TABLE: MultimapTableDefinition<&str, &str> =
|
||||
MultimapTableDefinition::new("pending_proofs");
|
||||
const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
|
||||
const KEYSET_COUNTER: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter");
|
||||
#[cfg(feature = "nostr")]
|
||||
const NOSTR_LAST_CHECKED: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter");
|
||||
|
||||
const DATABASE_VERSION: u32 = 0;
|
||||
|
||||
@@ -64,6 +68,8 @@ impl RedbWalletDatabase {
|
||||
let _ = write_txn.open_table(MINT_KEYS_TABLE)?;
|
||||
let _ = write_txn.open_multimap_table(PROOFS_TABLE)?;
|
||||
let _ = write_txn.open_table(KEYSET_COUNTER)?;
|
||||
#[cfg(feature = "nostr")]
|
||||
let _ = write_txn.open_table(NOSTR_LAST_CHECKED)?;
|
||||
table.insert("db_version", "0")?;
|
||||
}
|
||||
}
|
||||
@@ -562,4 +568,45 @@ impl WalletDatabase for RedbWalletDatabase {
|
||||
|
||||
Ok(counter.map(|c| c.value()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
#[instrument(skip(self))]
|
||||
async fn get_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: &PublicKey,
|
||||
) -> Result<Option<u32>, Self::Err> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read().map_err(Error::from)?;
|
||||
let table = read_txn
|
||||
.open_table(NOSTR_LAST_CHECKED)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
let last_checked = table
|
||||
.get(verifying_key.to_string().as_str())
|
||||
.map_err(Error::from)?;
|
||||
|
||||
Ok(last_checked.map(|c| c.value()))
|
||||
}
|
||||
#[cfg(feature = "nostr")]
|
||||
#[instrument(skip(self))]
|
||||
async fn add_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: PublicKey,
|
||||
last_checked: u32,
|
||||
) -> Result<(), Self::Err> {
|
||||
let db = self.db.lock().await;
|
||||
let write_txn = db.begin_write().map_err(Error::from)?;
|
||||
{
|
||||
let mut table = write_txn
|
||||
.open_table(NOSTR_LAST_CHECKED)
|
||||
.map_err(Error::from)?;
|
||||
|
||||
table
|
||||
.insert(verifying_key.to_string().as_str(), last_checked)
|
||||
.map_err(Error::from)?;
|
||||
}
|
||||
write_txn.commit().map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ license.workspace = true
|
||||
default = ["mint", "wallet"]
|
||||
mint = []
|
||||
wallet = ["dep:reqwest"]
|
||||
nostr = ["dep:nostr-sdk"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
@@ -41,6 +42,10 @@ tracing = { version = "0.1", default-features = false, features = [
|
||||
thiserror = "1"
|
||||
url = "2.3"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
nostr-sdk = { version = "0.31.0", default-features = false, features = [
|
||||
"nip04",
|
||||
"nip44"
|
||||
], optional = true }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tokio = { workspace = true, features = [
|
||||
|
||||
@@ -9,8 +9,10 @@ use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
use crate::mint::MintKeySetInfo;
|
||||
#[cfg(any(feature = "nostr", feature = "mint"))]
|
||||
use crate::nuts::PublicKey;
|
||||
#[cfg(feature = "mint")]
|
||||
use crate::nuts::{BlindSignature, CurrencyUnit, Proof, PublicKey};
|
||||
use crate::nuts::{BlindSignature, CurrencyUnit, Proof};
|
||||
#[cfg(any(feature = "wallet", feature = "mint"))]
|
||||
use crate::nuts::{Id, MintInfo};
|
||||
#[cfg(feature = "wallet")]
|
||||
@@ -91,6 +93,18 @@ pub trait WalletDatabase {
|
||||
|
||||
async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>;
|
||||
async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<Option<u32>, Self::Err>;
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
async fn get_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: &PublicKey,
|
||||
) -> Result<Option<u32>, Self::Err>;
|
||||
#[cfg(feature = "nostr")]
|
||||
async fn add_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: PublicKey,
|
||||
last_checked: u32,
|
||||
) -> Result<(), Self::Err>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
|
||||
@@ -8,10 +8,13 @@ use tokio::sync::RwLock;
|
||||
|
||||
use super::WalletDatabase;
|
||||
use crate::cdk_database::Error;
|
||||
#[cfg(feature = "nostr")]
|
||||
use crate::nuts::PublicKey;
|
||||
use crate::nuts::{Id, KeySetInfo, Keys, MintInfo, Proof, Proofs};
|
||||
use crate::types::{MeltQuote, MintQuote};
|
||||
use crate::url::UncheckedUrl;
|
||||
|
||||
// TODO: Change these all to RwLocks
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct WalletMemoryDatabase {
|
||||
mints: Arc<RwLock<HashMap<UncheckedUrl, Option<MintInfo>>>>,
|
||||
@@ -22,6 +25,8 @@ pub struct WalletMemoryDatabase {
|
||||
proofs: Arc<RwLock<HashMap<UncheckedUrl, HashSet<Proof>>>>,
|
||||
pending_proofs: Arc<RwLock<HashMap<UncheckedUrl, HashSet<Proof>>>>,
|
||||
keyset_counter: Arc<RwLock<HashMap<Id, u32>>>,
|
||||
#[cfg(feature = "nostr")]
|
||||
nostr_last_checked: Arc<RwLock<HashMap<PublicKey, u32>>>,
|
||||
}
|
||||
|
||||
impl WalletMemoryDatabase {
|
||||
@@ -30,6 +35,7 @@ impl WalletMemoryDatabase {
|
||||
melt_quotes: Vec<MeltQuote>,
|
||||
mint_keys: Vec<Keys>,
|
||||
keyset_counter: HashMap<Id, u32>,
|
||||
#[cfg(feature = "nostr")] nostr_last_checked: HashMap<PublicKey, u32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mints: Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -46,6 +52,8 @@ impl WalletMemoryDatabase {
|
||||
proofs: Arc::new(RwLock::new(HashMap::new())),
|
||||
pending_proofs: Arc::new(RwLock::new(HashMap::new())),
|
||||
keyset_counter: Arc::new(RwLock::new(keyset_counter)),
|
||||
#[cfg(feature = "nostr")]
|
||||
nostr_last_checked: Arc::new(RwLock::new(nostr_last_checked)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,4 +241,30 @@ impl WalletDatabase for WalletMemoryDatabase {
|
||||
async fn get_keyset_counter(&self, id: &Id) -> Result<Option<u32>, Error> {
|
||||
Ok(self.keyset_counter.read().await.get(id).cloned())
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
async fn get_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: &PublicKey,
|
||||
) -> Result<Option<u32>, Self::Err> {
|
||||
Ok(self
|
||||
.nostr_last_checked
|
||||
.read()
|
||||
.await
|
||||
.get(verifying_key)
|
||||
.cloned())
|
||||
}
|
||||
#[cfg(feature = "nostr")]
|
||||
async fn add_nostr_last_checked(
|
||||
&self,
|
||||
verifying_key: PublicKey,
|
||||
last_checked: u32,
|
||||
) -> Result<(), Self::Err> {
|
||||
self.nostr_last_checked
|
||||
.write()
|
||||
.await
|
||||
.insert(verifying_key, last_checked);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ pub enum Error {
|
||||
Secp256k1(#[from] secp256k1::Error),
|
||||
#[error(transparent)]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[cfg(feature = "nostr")]
|
||||
#[error(transparent)]
|
||||
NostrKey(#[from] nostr_sdk::key::Error),
|
||||
#[error("Invalid public key size: expected={expected}, found={found}")]
|
||||
InvalidPublicKeySize { expected: usize, found: usize },
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use core::str::FromStr;
|
||||
use bitcoin::hashes::sha256::Hash as Sha256Hash;
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::secp256k1::schnorr::Signature;
|
||||
use bitcoin::secp256k1::{self, Message};
|
||||
use bitcoin::secp256k1::{self, Message, XOnlyPublicKey};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use super::Error;
|
||||
@@ -30,6 +30,30 @@ impl From<secp256k1::PublicKey> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
impl TryFrom<PublicKey> for nostr_sdk::PublicKey {
|
||||
type Error = Error;
|
||||
fn try_from(pubkey: PublicKey) -> Result<Self, Self::Error> {
|
||||
Ok(nostr_sdk::PublicKey::from_slice(&pubkey.to_bytes())?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
impl TryFrom<nostr_sdk::PublicKey> for PublicKey {
|
||||
type Error = Error;
|
||||
fn try_from(pubkey: nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
|
||||
(&pubkey).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
impl TryFrom<&nostr_sdk::PublicKey> for PublicKey {
|
||||
type Error = Error;
|
||||
fn try_from(pubkey: &nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
|
||||
PublicKey::from_slice(&pubkey.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Parse from `bytes`
|
||||
#[inline]
|
||||
@@ -70,6 +94,11 @@ impl PublicKey {
|
||||
self.inner.serialize_uncompressed()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_only_pubkey(&self) -> XOnlyPublicKey {
|
||||
self.inner.x_only_public_key().0
|
||||
}
|
||||
|
||||
/// Get public key as `hex` string
|
||||
#[inline]
|
||||
pub fn to_hex(&self) -> String {
|
||||
|
||||
@@ -32,6 +32,22 @@ impl From<secp256k1::SecretKey> for SecretKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
impl TryFrom<SecretKey> for nostr_sdk::SecretKey {
|
||||
type Error = Error;
|
||||
fn try_from(seckey: SecretKey) -> Result<Self, Self::Error> {
|
||||
(&seckey).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
impl TryFrom<&SecretKey> for nostr_sdk::SecretKey {
|
||||
type Error = Error;
|
||||
fn try_from(seckey: &SecretKey) -> Result<Self, Self::Error> {
|
||||
Ok(nostr_sdk::SecretKey::from_slice(&seckey.to_secret_bytes())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SecretKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_secret_hex())
|
||||
|
||||
@@ -9,6 +9,10 @@ use bitcoin::bip32::ExtendedPrivKey;
|
||||
use bitcoin::hashes::sha256::Hash as Sha256Hash;
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::Network;
|
||||
#[cfg(feature = "nostr")]
|
||||
use nostr_sdk::nips::nip04;
|
||||
#[cfg(feature = "nostr")]
|
||||
use nostr_sdk::{Filter, NostrSigner, RelayPoolNotification, Timestamp};
|
||||
use thiserror::Error;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -81,6 +85,12 @@ pub enum Error {
|
||||
Invoice(#[from] lightning_invoice::ParseOrSemanticError),
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
#[cfg(feature = "nostr")]
|
||||
#[error(transparent)]
|
||||
NostrClient(#[from] nostr_sdk::client::Error),
|
||||
#[cfg(feature = "nostr")]
|
||||
#[error(transparent)]
|
||||
NostrKey(#[from] nostr_sdk::key::Error),
|
||||
#[error("`{0}`")]
|
||||
Custom(String),
|
||||
}
|
||||
@@ -96,6 +106,8 @@ pub struct Wallet {
|
||||
pub client: HttpClient,
|
||||
pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||
xpriv: ExtendedPrivKey,
|
||||
#[cfg(feature = "nostr")]
|
||||
nostr_client: nostr_sdk::Client,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
@@ -105,13 +117,24 @@ impl Wallet {
|
||||
) -> Self {
|
||||
let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, seed)
|
||||
.expect("Could not create master key");
|
||||
|
||||
Self {
|
||||
client: HttpClient::new(),
|
||||
localstore,
|
||||
xpriv,
|
||||
#[cfg(feature = "nostr")]
|
||||
nostr_client: nostr_sdk::Client::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add nostr relays to client
|
||||
#[cfg(feature = "nostr")]
|
||||
#[instrument(skip(self))]
|
||||
pub async fn add_nostr_relays(&self, relays: Vec<String>) -> Result<(), Error> {
|
||||
self.nostr_client.add_relays(relays).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Total Balance of wallet
|
||||
#[instrument(skip(self))]
|
||||
pub async fn total_balance(&self) -> Result<Amount, Error> {
|
||||
@@ -676,7 +699,7 @@ impl Wallet {
|
||||
/// Create Swap Payload
|
||||
#[instrument(skip(self, proofs), fields(mint_url = %mint_url))]
|
||||
async fn create_swap(
|
||||
&mut self,
|
||||
&self,
|
||||
mint_url: &UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
amount: Option<Amount>,
|
||||
@@ -784,7 +807,7 @@ impl Wallet {
|
||||
};
|
||||
|
||||
Ok(self
|
||||
.proofs_to_token(
|
||||
.proof_to_token(
|
||||
mint_url.clone(),
|
||||
send_proofs.ok_or(Error::InsufficientFunds)?,
|
||||
memo,
|
||||
@@ -1003,7 +1026,7 @@ impl Wallet {
|
||||
/// Receive
|
||||
#[instrument(skip_all)]
|
||||
pub async fn receive(
|
||||
&mut self,
|
||||
&self,
|
||||
encoded_token: &str,
|
||||
signing_keys: Option<Vec<SecretKey>>,
|
||||
preimages: Option<Vec<String>>,
|
||||
@@ -1061,7 +1084,7 @@ impl Wallet {
|
||||
|
||||
for proof in &mut proofs {
|
||||
// Verify that proof DLEQ is valid
|
||||
{
|
||||
if proof.dleq.is_some() {
|
||||
let keys = self.get_keyset_keys(&token.mint, proof.keyset_id).await?;
|
||||
let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?;
|
||||
proof.verify_dleq(key)?;
|
||||
@@ -1145,8 +1168,131 @@ impl Wallet {
|
||||
Ok(total_amount)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
#[instrument(skip(self))]
|
||||
pub async fn nostr_receive(&self, nostr_signing_key: SecretKey) -> Result<Amount, Error> {
|
||||
use nostr_sdk::{Keys, Kind, RelayMessage};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
let verifying_key = nostr_signing_key.public_key();
|
||||
|
||||
let nostr_pubkey =
|
||||
nostr_sdk::PublicKey::from_hex(verifying_key.x_only_pubkey().to_string())?;
|
||||
|
||||
let filter = match self
|
||||
.localstore
|
||||
.get_nostr_last_checked(&verifying_key)
|
||||
.await?
|
||||
{
|
||||
Some(since) => Filter::new()
|
||||
.pubkey(nostr_pubkey)
|
||||
.since(Timestamp::from(since as u64)),
|
||||
None => Filter::new().pubkey(nostr_pubkey),
|
||||
};
|
||||
|
||||
self.nostr_client.connect().await;
|
||||
self.nostr_client.subscribe(vec![filter], None).await;
|
||||
|
||||
let tokens: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
|
||||
|
||||
// Handle subscription notifications with `handle_notifications` method
|
||||
self.nostr_client
|
||||
.handle_notifications(|notification| async {
|
||||
let mut exit = false;
|
||||
let keys = Keys::from_str(&nostr_signing_key.to_secret_hex()).unwrap();
|
||||
|
||||
match notification {
|
||||
RelayPoolNotification::Event {
|
||||
relay_url: _,
|
||||
subscription_id: _,
|
||||
event,
|
||||
} => {
|
||||
// Check kind
|
||||
if event.kind() == Kind::EncryptedDirectMessage {
|
||||
if let Ok(msg) = nip04::decrypt(
|
||||
keys.secret_key()?,
|
||||
event.author_ref(),
|
||||
event.content(),
|
||||
) {
|
||||
println!("DM: {msg}");
|
||||
|
||||
if let Some(token) = Self::token_from_text(&msg) {
|
||||
tokens.lock().await.insert(token.to_string());
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Impossible to decrypt direct message");
|
||||
}
|
||||
} else {
|
||||
println!("Other event: {:?}", event.kind);
|
||||
}
|
||||
}
|
||||
RelayPoolNotification::Message { relay_url, message } => match message {
|
||||
RelayMessage::Auth { challenge } => {
|
||||
self.nostr_client
|
||||
.set_signer(Some(NostrSigner::Keys(keys)))
|
||||
.await;
|
||||
let r = self.nostr_client.auth(challenge, relay_url).await?;
|
||||
|
||||
tracing::debug!("Event id: {r}");
|
||||
}
|
||||
RelayMessage::Notice { message } => {
|
||||
tracing::debug!("Notice: {message}");
|
||||
}
|
||||
RelayMessage::Ok {
|
||||
event_id,
|
||||
status: _,
|
||||
message: _,
|
||||
} => {
|
||||
println!("Ok: {:?}", event_id);
|
||||
}
|
||||
RelayMessage::EndOfStoredEvents(_sub_id) => {
|
||||
exit = true;
|
||||
}
|
||||
_ => {
|
||||
tracing::debug!("{:?}", message);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
tracing::debug!("{:?}", notification);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(exit) // Set to true to exit from the loop
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut total_received = Amount::ZERO;
|
||||
for token in tokens.lock().await.iter() {
|
||||
match self.receive(token, None, None).await {
|
||||
Ok(amount) => total_received += amount,
|
||||
Err(err) => {
|
||||
tracing::error!("Could not receive token: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.localstore
|
||||
.add_nostr_last_checked(verifying_key, unix_time() as u32)
|
||||
.await?;
|
||||
|
||||
Ok(total_received)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nostr")]
|
||||
fn token_from_text(text: &str) -> Option<&str> {
|
||||
let text = text.trim();
|
||||
if let Some(start) = text.find("cashu") {
|
||||
match text[start..].find(' ') {
|
||||
Some(end) => return Some(&text[start..(end + start)]),
|
||||
None => return Some(&text[start..]),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[instrument(skip(self, proofs), fields(mint_url = %mint_url))]
|
||||
pub fn proofs_to_token(
|
||||
pub fn proof_to_token(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
proofs: Proofs,
|
||||
@@ -1408,71 +1554,20 @@ impl Wallet {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(feature = "nostr")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::mint::Mint;
|
||||
use cashu::nuts::nut04;
|
||||
|
||||
#[test]
|
||||
fn test_wallet() {
|
||||
let mut mint = Mint::new(
|
||||
"supersecretsecret",
|
||||
"0/0/0/0",
|
||||
HashMap::new(),
|
||||
HashSet::new(),
|
||||
32,
|
||||
);
|
||||
fn test_token_from_text() {
|
||||
let text = " Here is some ecash: cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJhbW91bnQiOjIsInNlY3JldCI6ImI2Zjk1ODIxYmZlNjUyYjYwZGQ2ZjYwMDU4N2UyZjNhOTk4MzVhMGMyNWI4MTQzODNlYWIwY2QzOWFiNDFjNzUiLCJDIjoiMDI1YWU4ZGEyOTY2Y2E5OGVmYjA5ZDcwOGMxM2FiZmEwZDkxNGUwYTk3OTE4MmFjMzQ4MDllMjYxODY5YTBhNDJlIiwicmVzZXJ2ZWQiOmZhbHNlLCJpZCI6IjAwOWExZjI5MzI1M2U0MWUifSx7ImFtb3VudCI6Miwic2VjcmV0IjoiZjU0Y2JjNmNhZWZmYTY5MTUyOTgyM2M1MjU1MDkwYjRhMDZjNGQ3ZDRjNzNhNDFlZTFkNDBlM2ExY2EzZGZhNyIsIkMiOiIwMjMyMTIzN2JlYjcyMWU3NGI1NzcwNWE5MjJjNjUxMGQwOTYyYzAzNzlhZDM0OTJhMDYwMDliZTAyNjA5ZjA3NTAiLCJyZXNlcnZlZCI6ZmFsc2UsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSJ9LHsiYW1vdW50IjoxLCJzZWNyZXQiOiJhNzdhM2NjODY4YWM4ZGU3YmNiOWMxMzJmZWI3YzEzMDY4Nzg3ODk5Yzk3YTk2NWE2ZThkZTFiMzliMmQ2NmQ3IiwiQyI6IjAzMTY0YTMxNWVhNjM0NGE5NWI2NzM1NzBkYzg0YmZlMTQ2NDhmMTQwM2EwMDJiZmJlMDhlNWFhMWE0NDQ0YWE0MCIsInJlc2VydmVkIjpmYWxzZSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIn1dLCJtaW50IjoiaHR0cHM6Ly90ZXN0bnV0LmNhc2h1LnNwYWNlIn1dLCJ1bml0Ijoic2F0In0= fdfdfg
|
||||
sdfs";
|
||||
let token = Wallet::token_from_text(text).unwrap();
|
||||
|
||||
let keys = mint.active_keyset_pubkeys();
|
||||
let token_str = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJhbW91bnQiOjIsInNlY3JldCI6ImI2Zjk1ODIxYmZlNjUyYjYwZGQ2ZjYwMDU4N2UyZjNhOTk4MzVhMGMyNWI4MTQzODNlYWIwY2QzOWFiNDFjNzUiLCJDIjoiMDI1YWU4ZGEyOTY2Y2E5OGVmYjA5ZDcwOGMxM2FiZmEwZDkxNGUwYTk3OTE4MmFjMzQ4MDllMjYxODY5YTBhNDJlIiwicmVzZXJ2ZWQiOmZhbHNlLCJpZCI6IjAwOWExZjI5MzI1M2U0MWUifSx7ImFtb3VudCI6Miwic2VjcmV0IjoiZjU0Y2JjNmNhZWZmYTY5MTUyOTgyM2M1MjU1MDkwYjRhMDZjNGQ3ZDRjNzNhNDFlZTFkNDBlM2ExY2EzZGZhNyIsIkMiOiIwMjMyMTIzN2JlYjcyMWU3NGI1NzcwNWE5MjJjNjUxMGQwOTYyYzAzNzlhZDM0OTJhMDYwMDliZTAyNjA5ZjA3NTAiLCJyZXNlcnZlZCI6ZmFsc2UsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSJ9LHsiYW1vdW50IjoxLCJzZWNyZXQiOiJhNzdhM2NjODY4YWM4ZGU3YmNiOWMxMzJmZWI3YzEzMDY4Nzg3ODk5Yzk3YTk2NWE2ZThkZTFiMzliMmQ2NmQ3IiwiQyI6IjAzMTY0YTMxNWVhNjM0NGE5NWI2NzM1NzBkYzg0YmZlMTQ2NDhmMTQwM2EwMDJiZmJlMDhlNWFhMWE0NDQ0YWE0MCIsInJlc2VydmVkIjpmYWxzZSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIn1dLCJtaW50IjoiaHR0cHM6Ly90ZXN0bnV0LmNhc2h1LnNwYWNlIn1dLCJ1bml0Ijoic2F0In0=";
|
||||
|
||||
let client = Client::new("https://cashu-rs.thesimplekid.space/").unwrap();
|
||||
|
||||
let wallet = Wallet::new(client, keys.keys);
|
||||
|
||||
let blinded_messages = BlindedMessages::random(Amount::from_sat(64)).unwrap();
|
||||
|
||||
let mint_request = nut04::MintRequest {
|
||||
outputs: blinded_messages.blinded_messages.clone(),
|
||||
};
|
||||
|
||||
let res = mint.process_mint_request(mint_request).unwrap();
|
||||
|
||||
let proofs = wallet
|
||||
.process_split_response(blinded_messages, res.promises)
|
||||
.unwrap();
|
||||
for proof in &proofs {
|
||||
mint.verify_proof(proof).unwrap();
|
||||
}
|
||||
|
||||
let split = wallet.create_split(proofs.clone()).unwrap();
|
||||
|
||||
let split_request = split.split_payload;
|
||||
|
||||
let split_response = mint.process_split_request(split_request).unwrap();
|
||||
let p = split_response.promises;
|
||||
|
||||
let snd_proofs = wallet
|
||||
.process_split_response(split.blinded_messages, p.unwrap())
|
||||
.unwrap();
|
||||
|
||||
let mut error = false;
|
||||
for proof in &snd_proofs {
|
||||
if let Err(err) = mint.verify_proof(proof) {
|
||||
println!("{err}{:?}", serde_json::to_string(proof));
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if error {
|
||||
panic!()
|
||||
}
|
||||
assert_eq!(token, token_str)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -26,9 +26,11 @@ buildargs=(
|
||||
"-p cdk"
|
||||
"-p cdk --no-default-features"
|
||||
"-p cdk --no-default-features --features wallet"
|
||||
"-p cdk --no-default-features --features wallet --features nostr"
|
||||
"-p cdk --no-default-features --features mint"
|
||||
"-p cdk-redb"
|
||||
"-p cdk-redb --no-default-features --features wallet"
|
||||
"-p cdk-redb --no-default-features --features wallet --features nostr"
|
||||
"-p cdk-redb --no-default-features --features mint"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user