mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-07 06:06:02 +01:00
feat(NUT09-13): Add support for NUT09 and NUT13
This commit is contained in:
@@ -17,7 +17,7 @@ gloo = ["dep:gloo"]
|
||||
all-nuts = ["nut07", "nut08", "nut09", "nut10", "nut11", "nut13"]
|
||||
nut07 = ["cashu/nut07"]
|
||||
nut08 = ["cashu/nut08"]
|
||||
nut09 = ["cashu/nut09"]
|
||||
nut09 = ["cashu/nut07", "cashu/nut09"]
|
||||
nut10 = ["cashu/nut10"]
|
||||
nut11 = ["cashu/nut11"]
|
||||
nut13 = ["cashu/nut13"]
|
||||
|
||||
@@ -288,10 +288,9 @@ impl LocalStore for RedbLocalStore {
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(SPENT_PROOFS_TABLE)?;
|
||||
let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?.into();
|
||||
table.insert(
|
||||
hash_to_curve(&proof.secret.to_bytes())?
|
||||
.to_sec1_bytes()
|
||||
.as_ref(),
|
||||
y.to_bytes().as_ref(),
|
||||
serde_json::to_string(&proof)?.as_str(),
|
||||
)?;
|
||||
}
|
||||
@@ -320,9 +319,9 @@ impl LocalStore for RedbLocalStore {
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(SPENT_PROOFS_TABLE)?;
|
||||
|
||||
let secret_hash = hash_to_curve(&secret.to_bytes())?;
|
||||
let y: PublicKey = hash_to_curve(&secret.to_bytes())?.into();
|
||||
|
||||
let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?;
|
||||
let proof = table.get(y.to_bytes().as_ref())?;
|
||||
|
||||
debug!("Checking secret: {}", secret.to_string());
|
||||
|
||||
@@ -446,6 +445,8 @@ impl LocalStore for RedbLocalStore {
|
||||
for blinded_message in blinded_messages {
|
||||
if let Some(blinded_signature) = table.get(blinded_message.to_bytes().as_ref())? {
|
||||
signatures.push(Some(serde_json::from_str(blinded_signature.value())?))
|
||||
} else {
|
||||
signatures.push(None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ use cashu::nuts::{
|
||||
};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::{CheckStateRequest, CheckStateResponse};
|
||||
#[cfg(feature = "nut09")]
|
||||
use cashu::nuts::{RestoreRequest, RestoreResponse};
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
use cashu::Amount;
|
||||
use http::StatusCode;
|
||||
@@ -291,6 +293,7 @@ impl Mint {
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
error!("Output has already been signed: {}", blinded_message.b);
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
@@ -373,6 +376,7 @@ impl Mint {
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
error!("Output has already been signed: {}", blinded_message.b);
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
@@ -658,6 +662,7 @@ impl Mint {
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
error!("Output has already been signed: {}", blinded_message.b);
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
@@ -740,13 +745,15 @@ impl Mint {
|
||||
let mut outputs = Vec::with_capacity(output_len);
|
||||
let mut signatures = Vec::with_capacity(output_len);
|
||||
|
||||
let blinded_message = request.outputs.iter().map(|b| b.b.clone()).collect();
|
||||
let blinded_message: Vec<PublicKey> = request.outputs.iter().map(|b| b.b.clone()).collect();
|
||||
|
||||
let blinded_signatures = self
|
||||
.localstore
|
||||
.get_blinded_signatures(blinded_message)
|
||||
.await?;
|
||||
|
||||
assert_eq!(blinded_signatures.len(), output_len);
|
||||
|
||||
for (blinded_message, blinded_signature) in
|
||||
request.outputs.into_iter().zip(blinded_signatures)
|
||||
{
|
||||
|
||||
@@ -211,8 +211,13 @@ impl LocalStore for MemoryLocalStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> {
|
||||
self.keyset_counter.lock().await.insert(*keyset_id, count);
|
||||
async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> {
|
||||
let keyset_counter = self.keyset_counter.lock().await;
|
||||
let current_counter = keyset_counter.get(keyset_id).unwrap_or(&0);
|
||||
self.keyset_counter
|
||||
.lock()
|
||||
.await
|
||||
.insert(*keyset_id, current_counter + count);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ pub trait LocalStore {
|
||||
) -> Result<(), Error>;
|
||||
|
||||
#[cfg(feature = "nut13")]
|
||||
async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error>;
|
||||
async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error>;
|
||||
#[cfg(feature = "nut13")]
|
||||
async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<Option<u64>, Error>;
|
||||
}
|
||||
|
||||
@@ -389,15 +389,27 @@ impl LocalStore for RedbLocalStore {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nut13")]
|
||||
async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> {
|
||||
async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
let current_counter;
|
||||
{
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(KEYSET_COUNTER)?;
|
||||
let counter = table.get(keyset_id.to_string().as_str())?;
|
||||
current_counter = if let Some(counter) = counter {
|
||||
counter.value()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
let write_txn = db.begin_write()?;
|
||||
{
|
||||
let mut table = write_txn.open_table(KEYSET_COUNTER)?;
|
||||
let new_counter = current_counter + count;
|
||||
|
||||
table.insert(keyset_id.to_string().as_str(), count)?;
|
||||
table.insert(keyset_id.to_string().as_str(), new_counter)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
|
||||
@@ -351,10 +351,9 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
self.localstore.remove_mint_quote("e_info.id).await?;
|
||||
|
||||
// Update counter for keyset
|
||||
if let Some(counter) = counter {
|
||||
let count = counter + proofs.len() as u64;
|
||||
if counter.is_some() {
|
||||
self.localstore
|
||||
.add_keyset_counter(&active_keyset_id, count)
|
||||
.increment_keyset_counter(&active_keyset_id, proofs.len() as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -406,6 +405,13 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
pre_swap.pre_mint_secrets.secrets(),
|
||||
&keys,
|
||||
)?;
|
||||
|
||||
if self.mnemonic.is_some() {
|
||||
self.localstore
|
||||
.increment_keyset_counter(&active_keyset_id, p.len() as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let mint_proofs = proofs.entry(token.mint).or_default();
|
||||
|
||||
mint_proofs.extend(p);
|
||||
@@ -523,14 +529,8 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
|
||||
if self.mnemonic.is_some() {
|
||||
for (keyset_id, count) in proof_count {
|
||||
let counter = self
|
||||
.localstore
|
||||
.get_keyset_counter(&keyset_id)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
self.localstore
|
||||
.add_keyset_counter(&keyset_id, counter + count)
|
||||
.increment_keyset_counter(&keyset_id, count)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
@@ -569,16 +569,8 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
let active_keyset = self.active_mint_keyset(mint_url, unit).await?;
|
||||
|
||||
if self.mnemonic.is_some() {
|
||||
let count = self
|
||||
.localstore
|
||||
.get_keyset_counter(&active_keyset)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let new_count = count + post_swap_proofs.len() as u64;
|
||||
|
||||
self.localstore
|
||||
.add_keyset_counter(&active_keyset, new_count)
|
||||
.increment_keyset_counter(&active_keyset, post_swap_proofs.len() as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -776,11 +768,14 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
};
|
||||
|
||||
if let Some(change_proofs) = change_proofs {
|
||||
debug!(
|
||||
"Change amount returned from melt: {}",
|
||||
change_proofs.iter().map(|p| p.amount).sum::<Amount>()
|
||||
);
|
||||
// Update counter for keyset
|
||||
if let Some(counter) = counter {
|
||||
let count = counter + change_proofs.len() as u64;
|
||||
if counter.is_some() {
|
||||
self.localstore
|
||||
.add_keyset_counter(&active_keyset_id, count)
|
||||
.increment_keyset_counter(&active_keyset_id, change_proofs.len() as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -951,6 +946,12 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
)?;
|
||||
let mint_proofs = received_proofs.entry(token.mint).or_default();
|
||||
|
||||
if self.mnemonic.is_some() {
|
||||
self.localstore
|
||||
.increment_keyset_counter(&active_keyset_id, p.len() as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
mint_proofs.extend(p);
|
||||
}
|
||||
|
||||
@@ -1014,6 +1015,7 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
|
||||
if response.signatures.is_empty() {
|
||||
empty_batch += 1;
|
||||
start_counter += 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1039,8 +1041,10 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
&keys,
|
||||
)?;
|
||||
|
||||
debug!("Restored {} proofs", proofs.len());
|
||||
|
||||
self.localstore
|
||||
.add_keyset_counter(&keyset.id, start_counter + proofs.len() as u64)
|
||||
.increment_keyset_counter(&keyset.id, proofs.len() as u64)
|
||||
.await?;
|
||||
|
||||
let states = self
|
||||
|
||||
@@ -123,6 +123,7 @@ mod mint {
|
||||
use std::ops::Mul;
|
||||
|
||||
use k256::{Scalar, SecretKey};
|
||||
use log::warn;
|
||||
|
||||
use super::hash_to_curve;
|
||||
use crate::error;
|
||||
@@ -154,6 +155,8 @@ mod mint {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
warn!("Message not verifed");
|
||||
|
||||
Err(error::mint::Error::TokenNotVerifed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use bip32::{DerivationPath, XPrv};
|
||||
use bip39::Mnemonic;
|
||||
use log::debug;
|
||||
|
||||
use super::{Id, SecretKey};
|
||||
use crate::error::Error;
|
||||
@@ -9,6 +10,11 @@ use crate::secret::Secret;
|
||||
|
||||
impl Secret {
|
||||
pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result<Self, Error> {
|
||||
debug!(
|
||||
"Deriving secret for {} with count {}",
|
||||
keyset_id.to_string(),
|
||||
counter.to_string()
|
||||
);
|
||||
let path = DerivationPath::from_str(&format!(
|
||||
"m/129372'/0'/{}'/{}'/0",
|
||||
u64::try_from(keyset_id)?,
|
||||
@@ -23,6 +29,11 @@ impl Secret {
|
||||
|
||||
impl SecretKey {
|
||||
pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result<Self, Error> {
|
||||
debug!(
|
||||
"Deriving key for {} with count {}",
|
||||
keyset_id.to_string(),
|
||||
counter.to_string()
|
||||
);
|
||||
let path = DerivationPath::from_str(&format!(
|
||||
"m/129372'/0'/{}'/{}'/1",
|
||||
u64::try_from(keyset_id)?,
|
||||
@@ -95,7 +106,7 @@ mod wallet {
|
||||
) -> Result<Self, wallet::Error> {
|
||||
let mut pre_mint_secrets = PreMintSecrets::default();
|
||||
|
||||
for i in start_count..end_count {
|
||||
for i in start_count..=end_count {
|
||||
let secret = Secret::from_seed(mnemonic, keyset_id, i)?;
|
||||
let blinding_factor = SecretKey::from_seed(mnemonic, keyset_id, i)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user