mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-07 14:15:56 +01:00
feat: nut13 restore for active keyset
This commit is contained in:
@@ -14,9 +14,10 @@ default = ["mint", "wallet", "all-nuts", "redb"]
|
||||
mint = ["cashu/mint"]
|
||||
wallet = ["cashu/wallet", "dep:minreq", "dep:once_cell"]
|
||||
gloo = ["dep:gloo"]
|
||||
all-nuts = ["nut07", "nut08", "nut10", "nut11", "nut13"]
|
||||
all-nuts = ["nut07", "nut08", "nut09", "nut10", "nut11", "nut13"]
|
||||
nut07 = ["cashu/nut07"]
|
||||
nut08 = ["cashu/nut08"]
|
||||
nut09 = ["cashu/nut09"]
|
||||
nut10 = ["cashu/nut10"]
|
||||
nut11 = ["cashu/nut11"]
|
||||
nut13 = ["cashu/nut13"]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! gloo wasm http Client
|
||||
|
||||
use async_trait::async_trait;
|
||||
#[cfg(feature = "nut09")]
|
||||
use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
|
||||
use cashu::nuts::{
|
||||
BlindedMessage, MeltBolt11Request, MeltBolt11Response, MintBolt11Request, MintBolt11Response,
|
||||
MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *,
|
||||
@@ -254,4 +256,32 @@ impl Client for HttpClient {
|
||||
Err(_) => Err(Error::from_json(&res.to_string())?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore [NUT-09]
|
||||
#[cfg(feature = "nut09")]
|
||||
async fn post_check_state(
|
||||
&self,
|
||||
mint_url: Url,
|
||||
request: RestoreRequest,
|
||||
) -> Result<CheckStateResponse, Error> {
|
||||
let url = join_url(mint_url, &["v1", "check"])?;
|
||||
|
||||
let res = Request::post(url.as_str())
|
||||
.json(&request)
|
||||
.map_err(|err| Error::Gloo(err.to_string()))?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| Error::Gloo(err.to_string()))?
|
||||
.json::<Value>()
|
||||
.await
|
||||
.map_err(|err| Error::Gloo(err.to_string()))?;
|
||||
|
||||
let response: Result<RestoreRequest, serde_json::Error> =
|
||||
serde_json::from_value(res.clone());
|
||||
|
||||
match response {
|
||||
Ok(res) => Ok(res),
|
||||
Err(_) => Err(Error::from_json(&res.to_string())?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::error::ErrorResponse;
|
||||
#[cfg(feature = "nut09")]
|
||||
use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::PublicKey;
|
||||
use cashu::nuts::{
|
||||
@@ -221,4 +223,26 @@ impl Client for HttpClient {
|
||||
Err(_) => Err(ErrorResponse::from_json(&res.to_string())?.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nut09")]
|
||||
async fn post_restore(
|
||||
&self,
|
||||
mint_url: Url,
|
||||
request: RestoreRequest,
|
||||
) -> Result<RestoreResponse, Error> {
|
||||
let url = join_url(mint_url, &["v1", "restore"])?;
|
||||
|
||||
let res = minreq::post(url)
|
||||
.with_json(&request)?
|
||||
.send()?
|
||||
.json::<Value>()?;
|
||||
|
||||
let response: Result<RestoreResponse, serde_json::Error> =
|
||||
serde_json::from_value(res.clone());
|
||||
|
||||
match response {
|
||||
Ok(res) => Ok(res),
|
||||
Err(_) => Err(ErrorResponse::from_json(&res.to_string())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::error::ErrorResponse;
|
||||
#[cfg(feature = "nut09")]
|
||||
use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::CheckStateResponse;
|
||||
#[cfg(feature = "nut07")]
|
||||
@@ -111,6 +113,13 @@ pub trait Client {
|
||||
) -> Result<CheckStateResponse, Error>;
|
||||
|
||||
async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;
|
||||
|
||||
#[cfg(feature = "nut09")]
|
||||
async fn post_restore(
|
||||
&self,
|
||||
mint_url: Url,
|
||||
restore_request: RestoreRequest,
|
||||
) -> Result<RestoreResponse, Error>;
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "wasm32"), feature = "gloo"))]
|
||||
|
||||
@@ -6,6 +6,9 @@ use bip39::Mnemonic;
|
||||
use cashu::dhke::{construct_proofs, unblind_message};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::nut07::ProofState;
|
||||
use cashu::nuts::nut07::State;
|
||||
#[cfg(feature = "nut09")]
|
||||
use cashu::nuts::nut09::RestoreRequest;
|
||||
use cashu::nuts::nut11::SigningKey;
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::PublicKey;
|
||||
@@ -18,7 +21,7 @@ use cashu::url::UncheckedUrl;
|
||||
use cashu::{Amount, Bolt11Invoice};
|
||||
use localstore::LocalStore;
|
||||
use thiserror::Error;
|
||||
use tracing::warn;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::utils::unix_time;
|
||||
@@ -299,8 +302,13 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
let count = self
|
||||
.localstore
|
||||
.get_keyset_counter(&active_keyset_id)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
.await?;
|
||||
|
||||
let count = if let Some(count) = count {
|
||||
count + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
counter = Some(count);
|
||||
PreMintSecrets::from_seed(
|
||||
@@ -427,8 +435,14 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
let count = self
|
||||
.localstore
|
||||
.get_keyset_counter(&active_keyset_id)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
.await?;
|
||||
|
||||
let count = if let Some(count) = count {
|
||||
count + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let premint_secrets = PreMintSecrets::from_seed(
|
||||
active_keyset_id,
|
||||
count,
|
||||
@@ -444,11 +458,11 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
PreMintSecrets::random(active_keyset_id, desired_amount)?
|
||||
};
|
||||
|
||||
if let (Some(amt), Some(mnemonic)) = (amount, &self.mnemonic) {
|
||||
if let Some(amt) = amount {
|
||||
let change_amount = proofs_total - amt;
|
||||
|
||||
let change_messages = if let Some(count) = counter {
|
||||
PreMintSecrets::from_seed(active_keyset_id, count, mnemonic, desired_amount, false)?
|
||||
let change_messages = if let (Some(count), Some(mnemonic)) = (counter, &self.mnemonic) {
|
||||
PreMintSecrets::from_seed(active_keyset_id, count, mnemonic, change_amount, false)?
|
||||
} else {
|
||||
PreMintSecrets::random(active_keyset_id, change_amount)?
|
||||
};
|
||||
@@ -547,6 +561,22 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
&self.active_keys(mint_url, unit).await?.unwrap(),
|
||||
)?;
|
||||
|
||||
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)
|
||||
.await?;
|
||||
}
|
||||
|
||||
post_swap_proofs.reverse();
|
||||
|
||||
for proof in post_swap_proofs {
|
||||
@@ -700,8 +730,13 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
let count = self
|
||||
.localstore
|
||||
.get_keyset_counter(&active_keyset_id)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
.await?;
|
||||
|
||||
let count = if let Some(count) = count {
|
||||
count + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
counter = Some(count);
|
||||
PreMintSecrets::from_seed(active_keyset_id, count, mnemonic, proofs_amount, true)?
|
||||
@@ -930,6 +965,102 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
) -> Result<String, Error> {
|
||||
Ok(Token::new(mint_url, proofs, memo, unit)?.to_string())
|
||||
}
|
||||
|
||||
pub async fn restore(&mut self, mint_url: UncheckedUrl) -> Result<Amount, Error> {
|
||||
// Check that mint is in store of mints
|
||||
if self.localstore.get_mint(mint_url.clone()).await?.is_none() {
|
||||
self.add_mint(mint_url.clone()).await?;
|
||||
}
|
||||
|
||||
let active_keyset_id = &self
|
||||
.active_mint_keyset(&mint_url, &CurrencyUnit::Sat)
|
||||
.await?;
|
||||
let keys = if let Some(keys) = self.localstore.get_keys(&active_keyset_id).await? {
|
||||
keys
|
||||
} else {
|
||||
self.get_mint_keys(&mint_url, *active_keyset_id).await?;
|
||||
self.localstore.get_keys(&active_keyset_id).await?.unwrap()
|
||||
};
|
||||
|
||||
let mut empty_batch = 0;
|
||||
let mut start_counter = 0;
|
||||
let mut restored_value = Amount::ZERO;
|
||||
|
||||
while empty_batch.lt(&3) {
|
||||
let premint_secrets = PreMintSecrets::restore_batch(
|
||||
*active_keyset_id,
|
||||
&self.mnemonic.clone().unwrap(),
|
||||
start_counter,
|
||||
start_counter + 100,
|
||||
)?;
|
||||
|
||||
debug!(
|
||||
"Attempting to restore counter {}-{} for mint {} keyset {}",
|
||||
start_counter,
|
||||
start_counter + 100,
|
||||
mint_url,
|
||||
active_keyset_id
|
||||
);
|
||||
|
||||
let restore_request = RestoreRequest {
|
||||
outputs: premint_secrets.blinded_messages(),
|
||||
};
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.post_restore(mint_url.clone().try_into()?, restore_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if response.signatures.is_empty() {
|
||||
empty_batch += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let premint_secrets: Vec<_> = premint_secrets
|
||||
.secrets
|
||||
.iter()
|
||||
.filter(|p| response.outputs.contains(&p.blinded_message))
|
||||
.collect();
|
||||
|
||||
// the response outputs and premint secrets should be the same after filtering
|
||||
// blinded messages the mint did not have signatures for
|
||||
assert_eq!(response.outputs.len(), premint_secrets.len());
|
||||
|
||||
let proofs = construct_proofs(
|
||||
response.signatures,
|
||||
premint_secrets.iter().map(|p| p.r.clone()).collect(),
|
||||
premint_secrets.iter().map(|p| p.secret.clone()).collect(),
|
||||
&keys,
|
||||
)?;
|
||||
|
||||
self.localstore
|
||||
.add_keyset_counter(active_keyset_id, start_counter + proofs.len() as u64)
|
||||
.await?;
|
||||
|
||||
let states = self
|
||||
.check_proofs_spent(mint_url.clone(), proofs.clone())
|
||||
.await?;
|
||||
|
||||
let unspent_proofs: Vec<Proof> = proofs
|
||||
.iter()
|
||||
.zip(states)
|
||||
.filter(|(_, state)| !state.state.eq(&State::Spent))
|
||||
.map(|(p, _)| p)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
restored_value += unspent_proofs.iter().map(|p| p.amount).sum();
|
||||
|
||||
self.localstore
|
||||
.add_proofs(mint_url.clone(), unspent_proofs)
|
||||
.await?;
|
||||
|
||||
empty_batch = 0;
|
||||
start_counter += 100;
|
||||
}
|
||||
Ok(restored_value)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -31,10 +31,10 @@ pub struct BlindedMessage {
|
||||
/// Witness
|
||||
#[cfg(feature = "nut11")]
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Signatures::is_empty")]
|
||||
#[serde(serialize_with = "witness_serialize")]
|
||||
#[serde(deserialize_with = "witness_deserialize")]
|
||||
pub witness: Signatures,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
//#[serde(serialize_with = "witness_serialize")]
|
||||
//#[serde(deserialize_with = "witness_deserialize")]
|
||||
pub witness: Option<Signatures>,
|
||||
}
|
||||
|
||||
impl BlindedMessage {
|
||||
@@ -44,7 +44,7 @@ impl BlindedMessage {
|
||||
keyset_id,
|
||||
b,
|
||||
#[cfg(feature = "nut11")]
|
||||
witness: Signatures::default(),
|
||||
witness: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,11 +437,12 @@ pub struct Proof {
|
||||
pub c: PublicKey,
|
||||
#[cfg(feature = "nut11")]
|
||||
/// Witness
|
||||
#[cfg(feature = "nut11")]
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Signatures::is_empty")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(serialize_with = "witness_serialize")]
|
||||
#[serde(deserialize_with = "witness_deserialize")]
|
||||
pub witness: Signatures,
|
||||
pub witness: Option<Signatures>,
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
@@ -452,7 +453,7 @@ impl Proof {
|
||||
secret,
|
||||
c,
|
||||
#[cfg(feature = "nut11")]
|
||||
witness: Signatures::default(),
|
||||
witness: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,5 +17,19 @@ pub struct RestoreResponse {
|
||||
/// Outputs
|
||||
pub outputs: Vec<BlindedMessage>,
|
||||
/// Signatures
|
||||
#[serde(rename = "promises")]
|
||||
pub signatures: Vec<BlindedSignature>,
|
||||
}
|
||||
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn restore_response() {
|
||||
use super::*;
|
||||
let rs = r#"{"outputs":[{"B_":"0204bbffa045f28ec836117a29ea0a00d77f1d692e38cf94f72a5145bfda6d8f41","amount":0,"id":"00ffd48b8f5ecf80", "witness":null},{"B_":"025f0615ccba96f810582a6885ffdb04bd57c96dbc590f5aa560447b31258988d7","amount":0,"id":"00ffd48b8f5ecf80"}],"promises":[{"C_":"02e9701b804dc05a5294b5a580b428237a27c7ee1690a0177868016799b1761c81","amount":8,"dleq":null,"id":"00ffd48b8f5ecf80"},{"C_":"031246ee046519b15648f1b8d8ffcb8e537409c84724e148c8d6800b2e62deb795","amount":2,"dleq":null,"id":"00ffd48b8f5ecf80"}]}"#;
|
||||
|
||||
let res: RestoreResponse = serde_json::from_str(rs).unwrap();
|
||||
|
||||
println!("{:?}", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,14 +32,14 @@ impl Signatures {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn witness_serialize<S>(x: &Signatures, s: S) -> Result<S::Ok, S::Error>
|
||||
pub fn witness_serialize<S>(x: &Option<Signatures>, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
s.serialize_str(&serde_json::to_string(x).map_err(ser::Error::custom)?)
|
||||
s.serialize_str(&serde_json::to_string(&x).map_err(ser::Error::custom)?)
|
||||
}
|
||||
|
||||
pub fn witness_deserialize<'de, D>(deserializer: D) -> Result<Signatures, D::Error>
|
||||
pub fn witness_deserialize<'de, D>(deserializer: D) -> Result<Option<Signatures>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
@@ -60,22 +60,23 @@ impl Proof {
|
||||
let mut valid_sigs = 0;
|
||||
|
||||
let msg = &self.secret.to_bytes();
|
||||
if let Some(witness) = &self.witness {
|
||||
for signature in &witness.signatures {
|
||||
let mut pubkeys = spending_conditions.pubkeys.clone();
|
||||
let data_key = VerifyingKey::from_str(&secret.secret_data.data)?;
|
||||
pubkeys.push(data_key);
|
||||
for v in &spending_conditions.pubkeys {
|
||||
let sig = Signature::try_from(hex::decode(signature)?.as_slice())?;
|
||||
|
||||
for signature in &self.witness.signatures {
|
||||
let mut pubkeys = spending_conditions.pubkeys.clone();
|
||||
let data_key = VerifyingKey::from_str(&secret.secret_data.data)?;
|
||||
pubkeys.push(data_key);
|
||||
for v in &spending_conditions.pubkeys {
|
||||
let sig = Signature::try_from(hex::decode(signature)?.as_slice())?;
|
||||
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
valid_sigs += 1;
|
||||
} else {
|
||||
debug!(
|
||||
"Could not verify signature: {} on message: {}",
|
||||
hex::encode(sig.to_bytes()),
|
||||
self.secret.to_string()
|
||||
)
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
valid_sigs += 1;
|
||||
} else {
|
||||
debug!(
|
||||
"Could not verify signature: {} on message: {}",
|
||||
hex::encode(sig.to_bytes()),
|
||||
self.secret.to_string()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,19 +85,19 @@ impl Proof {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{:?}", spending_conditions.refund_keys);
|
||||
|
||||
if let Some(locktime) = spending_conditions.locktime {
|
||||
// If lock time has passed check if refund witness signature is valid
|
||||
if locktime.lt(&unix_time()) && !spending_conditions.refund_keys.is_empty() {
|
||||
for s in &self.witness.signatures {
|
||||
for v in &spending_conditions.refund_keys {
|
||||
let sig = Signature::try_from(hex::decode(s)?.as_slice())
|
||||
.map_err(|_| Error::InvalidSignature)?;
|
||||
if let Some(signatures) = &self.witness {
|
||||
for s in &signatures.signatures {
|
||||
for v in &spending_conditions.refund_keys {
|
||||
let sig = Signature::try_from(hex::decode(s)?.as_slice())
|
||||
.map_err(|_| Error::InvalidSignature)?;
|
||||
|
||||
// As long as there is one valid refund signature it can be spent
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
return Ok(());
|
||||
// As long as there is one valid refund signature it can be spent
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +113,8 @@ impl Proof {
|
||||
let signature = secret_key.sign(msg_to_sign);
|
||||
|
||||
self.witness
|
||||
.as_mut()
|
||||
.unwrap_or(&mut Signatures::default())
|
||||
.signatures
|
||||
.push(hex::encode(signature.to_bytes()));
|
||||
|
||||
@@ -126,8 +129,11 @@ impl BlindedMessage {
|
||||
let signature = secret_key.sign(&msg_to_sign);
|
||||
|
||||
self.witness
|
||||
.as_mut()
|
||||
.unwrap_or(&mut Signatures::default())
|
||||
.signatures
|
||||
.push(hex::encode(signature.to_bytes()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -137,19 +143,21 @@ impl BlindedMessage {
|
||||
required_sigs: u64,
|
||||
) -> Result<(), Error> {
|
||||
let mut valid_sigs = 0;
|
||||
for signature in &self.witness.signatures {
|
||||
for v in pubkeys {
|
||||
let msg = &self.b.to_bytes();
|
||||
let sig = Signature::try_from(hex::decode(signature)?.as_slice())?;
|
||||
if let Some(witness) = &self.witness {
|
||||
for signature in &witness.signatures {
|
||||
for v in pubkeys {
|
||||
let msg = &self.b.to_bytes();
|
||||
let sig = Signature::try_from(hex::decode(signature)?.as_slice())?;
|
||||
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
valid_sigs += 1;
|
||||
} else {
|
||||
debug!(
|
||||
"Could not verify signature: {} on message: {}",
|
||||
hex::encode(sig.to_bytes()),
|
||||
self.b.to_string()
|
||||
)
|
||||
if v.verify(msg, &sig).is_ok() {
|
||||
valid_sigs += 1;
|
||||
} else {
|
||||
debug!(
|
||||
"Could not verify signature: {} on message: {}",
|
||||
hex::encode(sig.to_bytes()),
|
||||
self.b.to_string()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +757,7 @@ mod tests {
|
||||
"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
|
||||
)
|
||||
.unwrap(),
|
||||
witness: Signatures { signatures: vec![] },
|
||||
witness: Some(Signatures { signatures: vec![] }),
|
||||
};
|
||||
|
||||
proof.sign_p2pk(secret_key).unwrap();
|
||||
|
||||
@@ -84,6 +84,37 @@ mod wallet {
|
||||
|
||||
Ok(pre_mint_secrets)
|
||||
}
|
||||
|
||||
/// Generate blinded messages from predetermined secrets and blindings
|
||||
/// factor
|
||||
pub fn restore_batch(
|
||||
keyset_id: Id,
|
||||
mnemonic: &Mnemonic,
|
||||
start_count: u64,
|
||||
end_count: u64,
|
||||
) -> Result<Self, wallet::Error> {
|
||||
let mut pre_mint_secrets = PreMintSecrets::default();
|
||||
|
||||
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)?;
|
||||
|
||||
let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor.into()))?;
|
||||
|
||||
let blinded_message = BlindedMessage::new(Amount::ZERO, keyset_id, blinded);
|
||||
|
||||
let pre_mint = PreMint {
|
||||
blinded_message,
|
||||
secret: secret.clone(),
|
||||
r: r.into(),
|
||||
amount: Amount::ZERO,
|
||||
};
|
||||
|
||||
pre_mint_secrets.secrets.push(pre_mint);
|
||||
}
|
||||
|
||||
Ok(pre_mint_secrets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user