feat(NUT09-13): Add support for NUT09 and NUT13

This commit is contained in:
thesimplekid
2024-03-23 21:14:18 +00:00
parent 65b0f66729
commit a3faf2fb2e
9 changed files with 80 additions and 37 deletions

View File

@@ -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"]

View File

@@ -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);
}
}

View File

@@ -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)
{

View File

@@ -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(())
}

View File

@@ -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>;
}

View File

@@ -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()?;

View File

@@ -351,10 +351,9 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
self.localstore.remove_mint_quote(&quote_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

View File

@@ -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)
}
}

View File

@@ -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)?;