mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
feat: wallet create and unlock p2pk
nut11 verifying and signingkeys
This commit is contained in:
@@ -14,7 +14,7 @@ use cashu::nuts::{CheckStateRequest, CheckStateResponse};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::{Amount, Bolt11Invoice};
|
||||
use serde_json::Value;
|
||||
use tracing::{error, warn};
|
||||
use tracing::warn;
|
||||
use url::Url;
|
||||
|
||||
use super::join_url;
|
||||
@@ -170,19 +170,18 @@ impl Client for HttpClient {
|
||||
mint_url: Url,
|
||||
split_request: SwapRequest,
|
||||
) -> Result<SwapResponse, Error> {
|
||||
// TODO: Add to endpoint
|
||||
let url = join_url(mint_url, &["v1", "swap"])?;
|
||||
|
||||
let res = minreq::post(url).with_json(&split_request)?.send()?;
|
||||
|
||||
let value = res.json::<Value>()?;
|
||||
let response: Result<SwapResponse, serde_json::Error> =
|
||||
serde_json::from_value(res.json::<Value>()?.clone());
|
||||
serde_json::from_value(value.clone());
|
||||
|
||||
if let Err(err) = &response {
|
||||
error!("{}", err)
|
||||
match response {
|
||||
Ok(res) => Ok(res),
|
||||
Err(_) => Err(ErrorResponse::from_json(&value.to_string())?.into()),
|
||||
}
|
||||
|
||||
Ok(response?)
|
||||
}
|
||||
|
||||
/// Spendable check [NUT-07]
|
||||
|
||||
@@ -64,10 +64,10 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<Error> for ErrorResponse {
|
||||
fn from(_err: Error) -> ErrorResponse {
|
||||
fn from(err: Error) -> ErrorResponse {
|
||||
ErrorResponse {
|
||||
code: 9999,
|
||||
error: None,
|
||||
error: Some(err.to_string()),
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ use bip39::Mnemonic;
|
||||
use cashu::dhke::{construct_proofs, unblind_message};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::nuts::nut07::ProofState;
|
||||
use cashu::nuts::nut11::SigningKey;
|
||||
use cashu::nuts::{
|
||||
BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PreMintSecrets, PreSwap, Proof,
|
||||
Proofs, SwapRequest, Token,
|
||||
BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, P2PKConditions, PreMintSecrets,
|
||||
PreSwap, Proof, Proofs, SwapRequest, Token,
|
||||
};
|
||||
#[cfg(feature = "nut07")]
|
||||
use cashu::secret::Secret;
|
||||
@@ -363,7 +364,7 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
|
||||
let swap_response = self
|
||||
.client
|
||||
.post_swap(token.mint.clone().try_into()?, pre_swap.split_request)
|
||||
.post_swap(token.mint.clone().try_into()?, pre_swap.swap_request)
|
||||
.await?;
|
||||
|
||||
// Proof to keep
|
||||
@@ -393,9 +394,6 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
amount: Option<Amount>,
|
||||
proofs: Proofs,
|
||||
) -> Result<PreSwap, Error> {
|
||||
// Since swap is used to get the needed combination of tokens for a specific
|
||||
// amount first blinded messages are created for the amount
|
||||
|
||||
let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?.unwrap();
|
||||
|
||||
let pre_mint_secrets = if let Some(amount) = amount {
|
||||
@@ -415,11 +413,11 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
PreMintSecrets::random(active_keyset_id, value)?
|
||||
};
|
||||
|
||||
let split_request = SwapRequest::new(proofs, pre_mint_secrets.blinded_messages());
|
||||
let swap_request = SwapRequest::new(proofs, pre_mint_secrets.blinded_messages());
|
||||
|
||||
Ok(PreSwap {
|
||||
pre_mint_secrets,
|
||||
split_request,
|
||||
swap_request,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -471,7 +469,7 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
|
||||
let swap_response = self
|
||||
.client
|
||||
.post_swap(mint_url.clone().try_into()?, pre_swap.split_request)
|
||||
.post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
|
||||
.await?;
|
||||
|
||||
let mut keep_proofs = Proofs::new();
|
||||
@@ -553,7 +551,7 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
}
|
||||
|
||||
// Select proofs
|
||||
async fn select_proofs(
|
||||
pub async fn select_proofs(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
@@ -676,6 +674,118 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
|
||||
Ok(melted)
|
||||
}
|
||||
|
||||
/// Create P2PK locked proofs
|
||||
/// Uses a swap to swap proofs for locked p2pk conditions
|
||||
pub async fn create_p2pk_proofs(
|
||||
&mut self,
|
||||
mint_url: &UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
input_proofs: Proofs,
|
||||
conditions: P2PKConditions,
|
||||
) -> Result<Proofs, Error> {
|
||||
let amount = input_proofs.iter().map(|p| p.amount).sum();
|
||||
let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?.unwrap();
|
||||
|
||||
let pre_mint_secrets =
|
||||
PreMintSecrets::with_p2pk_conditions(active_keyset_id, amount, conditions)?;
|
||||
let swap_request =
|
||||
SwapRequest::new(input_proofs.clone(), pre_mint_secrets.blinded_messages());
|
||||
|
||||
let pre_swap = PreSwap {
|
||||
pre_mint_secrets,
|
||||
swap_request,
|
||||
};
|
||||
|
||||
let swap_response = self
|
||||
.client
|
||||
.post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
|
||||
.await?;
|
||||
|
||||
let post_swap_proofs = construct_proofs(
|
||||
swap_response.signatures,
|
||||
pre_swap.pre_mint_secrets.rs(),
|
||||
pre_swap.pre_mint_secrets.secrets(),
|
||||
&self.active_keys(mint_url, unit).await?.unwrap(),
|
||||
)?;
|
||||
|
||||
let mut send_proofs = vec![];
|
||||
let mut change_proofs = vec![];
|
||||
|
||||
for proof in post_swap_proofs {
|
||||
let conditions: Result<cashu::nuts::nut10::Secret, _> = (&proof.secret).try_into();
|
||||
println!("{:?}", conditions);
|
||||
if conditions.is_ok() {
|
||||
send_proofs.push(proof);
|
||||
} else {
|
||||
change_proofs.push(proof);
|
||||
}
|
||||
}
|
||||
|
||||
self.localstore
|
||||
.remove_proofs(mint_url.clone(), &input_proofs)
|
||||
.await?;
|
||||
|
||||
self.localstore
|
||||
.add_pending_proofs(mint_url.clone(), input_proofs)
|
||||
.await?;
|
||||
self.localstore
|
||||
.add_pending_proofs(mint_url.clone(), send_proofs.clone())
|
||||
.await?;
|
||||
self.localstore
|
||||
.add_proofs(mint_url.clone(), change_proofs.clone())
|
||||
.await?;
|
||||
|
||||
Ok(send_proofs)
|
||||
}
|
||||
|
||||
pub async fn claim_p2pk_locked_proof(
|
||||
&mut self,
|
||||
mint_url: &UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
signing_key: SigningKey,
|
||||
proofs: Proofs,
|
||||
) -> Result<(), Error> {
|
||||
let active_keyset_id = self.active_mint_keyset(&mint_url, &unit).await?;
|
||||
|
||||
let keys = self.localstore.get_keys(&active_keyset_id.unwrap()).await?;
|
||||
|
||||
let mut signed_proofs: Proofs = Vec::with_capacity(proofs.len());
|
||||
|
||||
// Sum amount of all proofs
|
||||
let amount: Amount = proofs.iter().map(|p| p.amount).sum();
|
||||
|
||||
for p in proofs.clone() {
|
||||
let mut p = p;
|
||||
p.sign_p2pk_proof(signing_key.clone()).unwrap();
|
||||
signed_proofs.push(p);
|
||||
}
|
||||
|
||||
let pre_swap = self
|
||||
.create_swap(mint_url, &unit, Some(amount), signed_proofs)
|
||||
.await?;
|
||||
|
||||
let swap_response = self
|
||||
.client
|
||||
.post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
|
||||
.await?;
|
||||
|
||||
// Proof to keep
|
||||
let p = construct_proofs(
|
||||
swap_response.signatures,
|
||||
pre_swap.pre_mint_secrets.rs(),
|
||||
pre_swap.pre_mint_secrets.secrets(),
|
||||
&keys.unwrap(),
|
||||
)?;
|
||||
|
||||
self.localstore
|
||||
.remove_proofs(mint_url.clone(), &proofs)
|
||||
.await?;
|
||||
|
||||
self.localstore.add_proofs(mint_url.clone(), p).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proofs_to_token(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
|
||||
@@ -229,7 +229,7 @@ mod tests {
|
||||
assert_eq!(sec, r.into());
|
||||
|
||||
assert_eq!(
|
||||
b.to_hex(),
|
||||
b.to_string(),
|
||||
PublicKey::from(
|
||||
k256::PublicKey::from_sec1_bytes(
|
||||
&hex::decode(
|
||||
@@ -239,7 +239,7 @@ mod tests {
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
.to_hex()
|
||||
.to_string()
|
||||
);
|
||||
|
||||
let message = "f1aaf16c2239746f369572c0784d9dd3d032d952c2d992175873fb58fae31a60";
|
||||
@@ -254,7 +254,7 @@ mod tests {
|
||||
assert_eq!(sec, r.into());
|
||||
|
||||
assert_eq!(
|
||||
b.to_hex(),
|
||||
b.to_string(),
|
||||
PublicKey::from(
|
||||
k256::PublicKey::from_sec1_bytes(
|
||||
&hex::decode(
|
||||
@@ -264,7 +264,7 @@ mod tests {
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
.to_hex()
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,6 @@ pub use nut08::{MeltBolt11Request, MeltBolt11Response};
|
||||
#[cfg(feature = "nut10")]
|
||||
pub use nut10::{Kind, Secret as Nut10Secret, SecretData};
|
||||
#[cfg(feature = "nut11")]
|
||||
pub use nut11::{P2PKConditions, Proof, SigFlag, Signatures};
|
||||
pub use nut11::{P2PKConditions, Proof, SigFlag, Signatures, SigningKey, VerifyingKey};
|
||||
|
||||
pub type Proofs = Vec<Proof>;
|
||||
|
||||
@@ -65,16 +65,6 @@ impl From<VerifyingKey> for PublicKey {
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
pub fn from_hex(hex: String) -> Result<Self, Error> {
|
||||
let hex = hex::decode(hex)?;
|
||||
Ok(PublicKey(k256::PublicKey::from_sec1_bytes(&hex)?))
|
||||
}
|
||||
|
||||
pub fn to_hex(&self) -> String {
|
||||
let bytes = self.0.to_sec1_bytes();
|
||||
hex::encode(bytes)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Box<[u8]> {
|
||||
self.0.to_sec1_bytes()
|
||||
}
|
||||
@@ -91,7 +81,8 @@ impl FromStr for PublicKey {
|
||||
|
||||
impl std::fmt::Display for PublicKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.to_hex())
|
||||
let bytes = self.0.to_sec1_bytes();
|
||||
f.write_str(&hex::encode(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,9 +271,12 @@ mod tests {
|
||||
#[test]
|
||||
fn pubkey() {
|
||||
let pubkey_str = "02c020067db727d586bc3183aecf97fcb800c3f4cc4759f69c626c9db5d8f5b5d4";
|
||||
let pubkey = PublicKey::from_str(pubkey_str).unwrap();
|
||||
assert_eq!(pubkey_str, pubkey.to_string());
|
||||
/*
|
||||
let pubkey_str = "04918dfc36c93e7db6cc0d60f37e1522f1c36b64d3f4b424c532d7c595febbc5";
|
||||
let pubkey = PublicKey::from_hex(pubkey_str.to_string()).unwrap();
|
||||
|
||||
assert_eq!(pubkey_str, pubkey.to_hex())
|
||||
assert_eq!(pubkey_str, pubkey.to_hex())*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,7 +14,7 @@ pub use crate::Bolt11Invoice;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct PreSwap {
|
||||
pub pre_mint_secrets: PreMintSecrets,
|
||||
pub split_request: SwapRequest,
|
||||
pub swap_request: SwapRequest,
|
||||
}
|
||||
|
||||
/// Split Request [NUT-06]
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::str::FromStr;
|
||||
|
||||
use bitcoin::hashes::{sha256, Hash};
|
||||
use k256::schnorr::signature::{Signer, Verifier};
|
||||
use k256::schnorr::{Signature, SigningKey, VerifyingKey};
|
||||
use k256::schnorr::Signature;
|
||||
use serde::de::Error as DeserializerError;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
@@ -16,17 +16,22 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use super::nut01::PublicKey;
|
||||
use super::nut02::Id;
|
||||
use super::nut10::{Secret, SecretData};
|
||||
use super::SecretKey;
|
||||
use crate::error::Error;
|
||||
use crate::utils::unix_time;
|
||||
use crate::Amount;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Signatures {
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
signatures: Vec<String>,
|
||||
}
|
||||
|
||||
fn no_signatures(signatures: &Signatures) -> bool {
|
||||
signatures.signatures.is_empty()
|
||||
impl Signatures {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.signatures.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Proofs [NUT-11]
|
||||
@@ -44,7 +49,7 @@ pub struct Proof {
|
||||
pub keyset_id: Id,
|
||||
/// Witness
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "no_signatures")]
|
||||
#[serde(skip_serializing_if = "Signatures::is_empty")]
|
||||
pub witness: Signatures,
|
||||
}
|
||||
|
||||
@@ -82,10 +87,10 @@ impl PartialOrd for Proof {
|
||||
pub struct P2PKConditions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub locktime: Option<u64>,
|
||||
pub pubkeys: Vec<PublicKey>,
|
||||
pub pubkeys: Vec<VerifyingKey>,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub refund_keys: Vec<PublicKey>,
|
||||
pub refund_keys: Vec<VerifyingKey>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_sigs: Option<u64>,
|
||||
pub sig_flag: SigFlag,
|
||||
@@ -94,8 +99,8 @@ pub struct P2PKConditions {
|
||||
impl P2PKConditions {
|
||||
pub fn new(
|
||||
locktime: Option<u64>,
|
||||
pubkeys: Vec<PublicKey>,
|
||||
refund_keys: Vec<PublicKey>,
|
||||
pubkeys: Vec<VerifyingKey>,
|
||||
refund_keys: Vec<VerifyingKey>,
|
||||
num_sigs: Option<u64>,
|
||||
sig_flag: Option<SigFlag>,
|
||||
) -> Result<Self, Error> {
|
||||
@@ -131,7 +136,7 @@ impl TryFrom<P2PKConditions> for Secret {
|
||||
return Err(Error::Amount);
|
||||
}
|
||||
|
||||
let data = pubkeys[0].to_hex();
|
||||
let data = pubkeys[0].to_string();
|
||||
|
||||
let mut tags = vec![];
|
||||
|
||||
@@ -187,14 +192,14 @@ impl TryFrom<Secret> for P2PKConditions {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut pubkeys: Vec<PublicKey> = vec![];
|
||||
let mut pubkeys: Vec<VerifyingKey> = vec![];
|
||||
|
||||
if let Some(Tag::PubKeys(keys)) = tags.get(&TagKind::Pubkeys) {
|
||||
let mut keys = keys.clone();
|
||||
pubkeys.append(&mut keys);
|
||||
}
|
||||
|
||||
let data_pubkey = PublicKey::from_hex(secret.secret_data.data)?;
|
||||
let data_pubkey = VerifyingKey::from_str(&secret.secret_data.data)?;
|
||||
pubkeys.push(data_pubkey);
|
||||
|
||||
let locktime = if let Some(tag) = tags.get(&TagKind::Locktime) {
|
||||
@@ -258,20 +263,15 @@ impl Proof {
|
||||
|
||||
for signature in &self.witness.signatures {
|
||||
let mut pubkeys = spending_conditions.pubkeys.clone();
|
||||
let data_key = PublicKey::from_str(&secret.secret_data.data).unwrap();
|
||||
let data_key = VerifyingKey::from_str(&secret.secret_data.data).unwrap();
|
||||
pubkeys.push(data_key);
|
||||
for v in &spending_conditions.pubkeys {
|
||||
let sig = Signature::try_from(hex::decode(signature).unwrap().as_slice()).unwrap();
|
||||
|
||||
let verifying_key: VerifyingKey = v.try_into()?;
|
||||
|
||||
if verifying_key.verify(&msg.to_byte_array(), &sig).is_ok() {
|
||||
if v.verify(&msg.to_byte_array(), &sig).is_ok() {
|
||||
valid_sigs += 1;
|
||||
} else {
|
||||
println!(
|
||||
"{:?}",
|
||||
verifying_key.verify(&msg.to_byte_array(), &sig).unwrap()
|
||||
);
|
||||
println!("{:?}", v.verify(&msg.to_byte_array(), &sig).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,6 @@ impl Proof {
|
||||
for v in &spending_conditions.refund_keys {
|
||||
let sig = Signature::try_from(s.as_bytes())
|
||||
.map_err(|_| Error::InvalidSignature)?;
|
||||
let v: VerifyingKey = v.clone().try_into()?;
|
||||
|
||||
// As long as there is one valid refund signature it can be spent
|
||||
if v.verify(&msg.to_byte_array(), &sig).is_ok() {
|
||||
@@ -398,8 +397,8 @@ pub enum Tag {
|
||||
SigFlag(SigFlag),
|
||||
NSigs(u64),
|
||||
LockTime(u64),
|
||||
Refund(Vec<PublicKey>),
|
||||
PubKeys(Vec<PublicKey>),
|
||||
Refund(Vec<VerifyingKey>),
|
||||
PubKeys(Vec<VerifyingKey>),
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
@@ -445,7 +444,7 @@ where
|
||||
let pubkeys = tag
|
||||
.iter()
|
||||
.skip(1)
|
||||
.flat_map(|p| PublicKey::from_hex(p.as_ref().to_string()))
|
||||
.flat_map(|p| VerifyingKey::from_str(p.as_ref()))
|
||||
.collect();
|
||||
|
||||
Ok(Self::Refund(pubkeys))
|
||||
@@ -454,7 +453,7 @@ where
|
||||
let pubkeys = tag
|
||||
.iter()
|
||||
.skip(1)
|
||||
.flat_map(|p| PublicKey::from_hex(p.as_ref().to_string()))
|
||||
.flat_map(|p| VerifyingKey::from_str(p.as_ref()))
|
||||
.collect();
|
||||
|
||||
Ok(Self::PubKeys(pubkeys))
|
||||
@@ -477,7 +476,7 @@ impl From<Tag> for Vec<String> {
|
||||
let mut tag = vec![TagKind::Pubkeys.to_string()];
|
||||
|
||||
for pubkey in pubkeys {
|
||||
tag.push(pubkey.to_hex())
|
||||
tag.push(pubkey.to_string())
|
||||
}
|
||||
tag
|
||||
}
|
||||
@@ -485,7 +484,7 @@ impl From<Tag> for Vec<String> {
|
||||
let mut tag = vec![TagKind::Refund.to_string()];
|
||||
|
||||
for pubkey in pubkeys {
|
||||
tag.push(pubkey.to_hex())
|
||||
tag.push(pubkey.to_string())
|
||||
}
|
||||
tag
|
||||
}
|
||||
@@ -518,33 +517,174 @@ impl<'de> Deserialize<'de> for Tag {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct VerifyingKey(k256::schnorr::VerifyingKey);
|
||||
|
||||
impl VerifyingKey {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Ok(VerifyingKey(
|
||||
k256::schnorr::VerifyingKey::from_bytes(bytes).unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
|
||||
Ok(self.0.verify(msg, signature).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for VerifyingKey {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(hex: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = hex::decode(hex)?;
|
||||
|
||||
let bytes = if bytes.len().eq(&33) {
|
||||
bytes.iter().skip(1).cloned().collect()
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
};
|
||||
|
||||
Ok(VerifyingKey(
|
||||
k256::schnorr::VerifyingKey::from_bytes(&bytes).map_err(|_| Error::Key)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VerifyingKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let bytes = self.0.to_bytes();
|
||||
f.write_str(&hex::encode(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerifyingKey> for k256::schnorr::VerifyingKey {
|
||||
fn from(value: VerifyingKey) -> k256::schnorr::VerifyingKey {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&VerifyingKey> for k256::schnorr::VerifyingKey {
|
||||
fn from(value: &VerifyingKey) -> k256::schnorr::VerifyingKey {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<k256::schnorr::VerifyingKey> for VerifyingKey {
|
||||
fn from(value: k256::schnorr::VerifyingKey) -> VerifyingKey {
|
||||
VerifyingKey(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PublicKey> for VerifyingKey {
|
||||
type Error = Error;
|
||||
fn try_from(value: PublicKey) -> Result<VerifyingKey, Self::Error> {
|
||||
(&value).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PublicKey> for VerifyingKey {
|
||||
type Error = Error;
|
||||
fn try_from(value: &PublicKey) -> Result<VerifyingKey, Self::Error> {
|
||||
let bytes = value.to_bytes();
|
||||
|
||||
let bytes = if bytes.len().eq(&33) {
|
||||
bytes.iter().skip(1).cloned().collect()
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
};
|
||||
|
||||
VerifyingKey::from_bytes(&bytes).map_err(|_| Error::Key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct SigningKey(k256::schnorr::SigningKey);
|
||||
|
||||
impl From<SigningKey> for k256::schnorr::SigningKey {
|
||||
fn from(value: SigningKey) -> k256::schnorr::SigningKey {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<k256::schnorr::SigningKey> for SigningKey {
|
||||
fn from(value: k256::schnorr::SigningKey) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretKey> for SigningKey {
|
||||
fn from(value: SecretKey) -> SigningKey {
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningKey {
|
||||
pub fn public_key(&self) -> VerifyingKey {
|
||||
self.0.verifying_key().clone().into()
|
||||
}
|
||||
|
||||
pub fn sign(&self, msg: &[u8]) -> Signature {
|
||||
self.0.sign(msg)
|
||||
}
|
||||
|
||||
pub fn verifying_key(&self) -> VerifyingKey {
|
||||
VerifyingKey(self.0.verifying_key().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SigningKey {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(hex: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = hex::decode(hex)?;
|
||||
|
||||
let bytes = if bytes.len().eq(&33) {
|
||||
bytes.iter().skip(1).cloned().collect()
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
};
|
||||
|
||||
Ok(SigningKey(
|
||||
k256::schnorr::SigningKey::from_bytes(&bytes).map_err(|_| Error::Key)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SigningKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let bytes = self.0.to_bytes();
|
||||
|
||||
f.write_str(&hex::encode(bytes))
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::*;
|
||||
use crate::nuts::SecretKey;
|
||||
|
||||
#[test]
|
||||
fn test_secret_ser() {
|
||||
let conditions = P2PKConditions {
|
||||
locktime: Some(99999),
|
||||
pubkeys: vec![
|
||||
PublicKey::from_str(
|
||||
VerifyingKey::from_str(
|
||||
"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
|
||||
)
|
||||
.unwrap(),
|
||||
PublicKey::from_str(
|
||||
VerifyingKey::from_str(
|
||||
"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
|
||||
)
|
||||
.unwrap(),
|
||||
PublicKey::from_str(
|
||||
VerifyingKey::from_str(
|
||||
"023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
refund_keys: vec![PublicKey::from_str(
|
||||
refund_keys: vec![VerifyingKey::from_str(
|
||||
"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
|
||||
)
|
||||
.unwrap()],
|
||||
@@ -572,13 +712,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn sign_proof() {
|
||||
let secret_key =
|
||||
SecretKey::from_hex("04918dfc36c93e7db6cc0d60f37e1522f1c36b64d3f4b424c532d7c595febbc5")
|
||||
.unwrap();
|
||||
let secret_key = SigningKey::from_str(
|
||||
"04918dfc36c93e7db6cc0d60f37e1522f1c36b64d3f4b424c532d7c595febbc5",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pubkey: PublicKey = secret_key.public_key();
|
||||
|
||||
let v_key: VerifyingKey = pubkey.clone().try_into().unwrap();
|
||||
let v_key: VerifyingKey = secret_key.verifying_key();
|
||||
|
||||
let conditions = P2PKConditions {
|
||||
locktime: None,
|
||||
|
||||
Reference in New Issue
Block a user