diff --git a/common/nym_offline_compact_ecash/src/scheme/mod.rs b/common/nym_offline_compact_ecash/src/scheme/mod.rs index 281cf7d984..53d3b73bea 100644 --- a/common/nym_offline_compact_ecash/src/scheme/mod.rs +++ b/common/nym_offline_compact_ecash/src/scheme/mod.rs @@ -433,6 +433,19 @@ impl Payment { Ok(true) } + pub fn serial_number_bs58(&self) -> String { + SerialNumber { + inner: self.ss.clone(), + } + .to_bs58() + } + + pub fn has_serial_number(&self, serial_number_bs58: &str) -> Result { + let serial_number = SerialNumber::try_from_bs58(serial_number_bs58)?; + let ret = self.ss.eq(&serial_number.inner); + Ok(ret) + } + pub fn to_bytes(&self) -> Vec { let kappa_bytes = self.kappa.to_affine().to_compressed(); let sig_bytes = self.sig.to_bytes(); @@ -640,6 +653,60 @@ impl TryFrom<&[u8]> for Payment { } } +pub struct SerialNumber { + pub(crate) inner: Vec, +} + +impl SerialNumber { + pub fn to_bytes(&self) -> Vec { + let ss_len = self.inner.len(); + let mut bytes: Vec = Vec::with_capacity(ss_len * 48); + for s in &self.inner { + bytes.extend_from_slice(&s.to_affine().to_compressed()); + } + bytes + } +} + +impl TryFrom<&[u8]> for SerialNumber { + type Error = CompactEcashError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() % 48 != 0 { + return Err( + CompactEcashError::Deserialization( + format!("Tried to deserialize blinded serial number with incorrect number of bytes, expected a multiple of 48, got {}", bytes.len()), + )); + } + let inner_len = bytes.len() / 48; + let mut inner = Vec::with_capacity(inner_len); + let mut idx = 0; + for _ in 0..inner_len { + let ss_bytes: [u8; 48] = bytes[idx..idx + 48].try_into().unwrap(); + let ss_elem = try_deserialize_g1_projective( + &ss_bytes, + CompactEcashError::Deserialization("Failed to deserialize ss element".to_string()), + )?; + inner.push(ss_elem); + idx += 48; + } + + Ok(SerialNumber { inner }) + } +} + +impl Bytable for SerialNumber { + fn to_byte_vec(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn try_from_byte_slice(slice: &[u8]) -> Result { + Self::try_from(slice) + } +} + +impl Base58 for SerialNumber {} + #[derive(Getters, CopyGetters)] pub struct EcashCredential { #[getset(get = "pub")] @@ -677,12 +744,12 @@ impl EcashCredential { pub fn value(&self) -> u64 { self.value } - pub fn blinded_serial_number(&self) -> String { - todo!() //SW todo + pub fn serial_number(&self) -> String { + self.payment.serial_number_bs58() } - pub fn has_blinded_serial_number(&self, _blinded_serial_number_bs58: &str) -> Result { - todo!() //SW todo + pub fn has_serial_number(&self, serial_number_bs58: &str) -> Result { + self.payment.has_serial_number(serial_number_bs58) } } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index c0978a4d28..dfc8f545a3 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -229,11 +229,8 @@ impl EcashVerifier { let res = self .nyxd_client .spend_credential( - Coin::new( - credential.value().into(), //SW THIS WILL BE A FIXED VALUE? - self.mix_denom_base.clone(), - ), - credential.blinded_serial_number(), //SW what will this be? + Coin::new(credential.value().into(), self.mix_denom_base.clone()), + credential.serial_number(), self.nyxd_client.address().to_string(), None, ) @@ -249,8 +246,7 @@ impl EcashVerifier { })?; let proposal = self.nyxd_client.query_proposal(proposal_id).await?; - if !credential.has_blinded_serial_number(&proposal.description)? { - //SW serial number issue + if !credential.has_serial_number(&proposal.description)? { return Err(RequestHandlingError::ProposalIdError { reason: String::from("proposal has different serial number"), }); diff --git a/nym-api/src/coconut/mod.rs b/nym-api/src/coconut/mod.rs index fed927a27d..2c8c75de47 100644 --- a/nym-api/src/coconut/mod.rs +++ b/nym-api/src/coconut/mod.rs @@ -304,7 +304,7 @@ pub async fn verify_online_credential( // Proposal description is the blinded serial number if !verify_credential_body .credential() - .has_blinded_serial_number(&proposal.description)? + .has_serial_number(&proposal.description)? { return Err(CoconutError::IncorrectProposal { reason: String::from("incorrect blinded serial number in description"), @@ -317,7 +317,7 @@ pub async fn verify_online_credential( // Credential has not been spent before, and is on its way of being spent let credential_status = state .client - .get_spent_credential(verify_credential_body.credential().blinded_serial_number()) + .get_spent_credential(verify_credential_body.credential().serial_number()) .await? .spend_credential .ok_or(CoconutError::InvalidCredentialStatus { @@ -344,7 +344,6 @@ pub async fn verify_online_credential( CoconutError::CompactEcashInternalError(CompactEcashError::PaymentVerification) })?; - //SW THIS WILL HAVE TO CHANGE AS WELL vote_yes &= Coin::from(proposed_release_funds) == Coin::new( verify_credential_body.credential().value() as u128,