From b16c197a3ba5bc91a3a9c24aaaf92947c9f6ded6 Mon Sep 17 00:00:00 2001 From: bwty Date: Tue, 27 Dec 2022 22:08:57 +0700 Subject: [PATCH] feat: revocation_list model with demo test Signed-off-by: bwty --- .gitignore | 1 + anoncreds/Cargo.toml | 3 +- anoncreds/src/data_types/anoncreds/rev_reg.rs | 133 ++++++- anoncreds/src/ffi/revocation.rs | 19 +- anoncreds/src/services/prover.rs | 109 ++++-- anoncreds/src/services/types.rs | 8 +- anoncreds/src/services/verifier.rs | 18 + anoncreds/tests/anoncreds_demos.rs | 332 +++++++++++++++++- 8 files changed, 580 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 8094f6ea..de85d710 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode target Cargo.lock +.tmp diff --git a/anoncreds/Cargo.toml b/anoncreds/Cargo.toml index 8c817b6c..66f43e73 100644 --- a/anoncreds/Cargo.toml +++ b/anoncreds/Cargo.toml @@ -29,11 +29,12 @@ once_cell = "1.9" rand = "0.8.5" regex = "1.2.1" serde = { version = "1.0", features = ["derive"] } +bitvec = { version = "1.0.1", features = ["serde"] } serde_json = { version = "1.0", features = ["raw_value"]} sha2 = "0.10" tempfile = "3.1.0" thiserror = "1.0" -ursa = { version = "=0.3.6", default-features = false, features = ["cl_native", "serde"] } +ursa = { path = "../../../ursa/libursa", default-features = false, features = ["cl_native", "serde"] } zeroize = { version = "1.3", optional = true, features = ["zeroize_derive"] } # We add the openssl dependency here because ursa does not expose a vendored openssl feature # Since we use "cl_native" as a feature, which uses openssl, we can add a vendored build with diff --git a/anoncreds/src/data_types/anoncreds/rev_reg.rs b/anoncreds/src/data_types/anoncreds/rev_reg.rs index e19a2188..4f378441 100644 --- a/anoncreds/src/data_types/anoncreds/rev_reg.rs +++ b/anoncreds/src/data_types/anoncreds/rev_reg.rs @@ -1,6 +1,12 @@ +use bitvec::vec::BitVec; +use serde::{ + de::{Deserializer, Error as DeError, SeqAccess, Visitor}, + ser::{SerializeSeq, Serializer}, +}; use std::collections::HashSet; -use crate::{data_types::Validatable, impl_anoncreds_object_identifier}; +use crate::{data_types::Validatable, error, impl_anoncreds_object_identifier}; +use ursa::cl::Accumulator; impl_anoncreds_object_identifier!(RevocationRegistryId); @@ -47,3 +53,128 @@ impl Validatable for RevocationRegistryDelta {} pub struct RevocationRegistryDeltaV1 { pub value: ursa::cl::RevocationRegistryDelta, } + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct RevocationList { + rev_reg_id: RevocationRegistryId, + #[serde(with = "serde_revocation_list")] + revocation_list: bitvec::vec::BitVec, + current_accum: Accumulator, + timestamp: u64, +} + +impl From<&RevocationList> for ursa::cl::RevocationRegistry { + fn from(rev_reg_list: &RevocationList) -> ursa::cl::RevocationRegistry { + ursa::cl::RevocationRegistry::from(rev_reg_list.current_accum) + } +} + +impl RevocationList { + pub(crate) fn timestamp(&self) -> u64 { + self.timestamp + } + + pub(crate) fn state(&self) -> &bitvec::vec::BitVec { + &self.revocation_list + } + + pub(crate) fn state_owned(&self) -> bitvec::vec::BitVec { + self.revocation_list.clone() + } + + pub fn new( + rev_reg_id: &str, + revocation_list: bitvec::vec::BitVec, + current_reg: ursa::cl::RevocationRegistry, + timestamp: u64, + ) -> Result { + Ok(Self { + rev_reg_id: RevocationRegistryId::new(rev_reg_id)?, + revocation_list, + current_accum: current_reg.into(), + timestamp, + }) + } +} + +pub mod serde_revocation_list { + use super::*; + pub fn serialize(state: &bitvec::vec::BitVec, s: S) -> Result + where + S: Serializer, + { + let mut seq = s.serialize_seq(Some(state.len()))?; + for element in state { + let e = match element.as_ref() { + &true => 1, + &false => 0, + }; + seq.serialize_element(&e)?; + } + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct JsonBitStringVisitor; + + impl<'de> Visitor<'de> for JsonBitStringVisitor { + type Value = bitvec::vec::BitVec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a seq containing revoation state, i.e. [1, 0, 1]") + } + + fn visit_seq(self, mut v: S) -> Result + where + S: SeqAccess<'de>, + { + // TODO: do we have a min size for this? + let mut bv = BitVec::with_capacity(v.size_hint().unwrap_or_default()); + while let Some(ele) = v.next_element()? { + match ele { + 0 => bv.push(false), + 1 => bv.push(true), + _ => { + return Err(S::Error::custom("invalid revocation state")); + } + } + } + Ok(bv) + } + } + deserializer.deserialize_seq(JsonBitStringVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bitvec::prelude::*; + + const REVOCATION_LIST: &str = "{\"revRegId\":\"reg\",\"revocationList\":[1,1,1,1],\"currentAccum\":\"1 1379509F4D411630D308A5ABB4F422FCE6737B330B1C5FD286AA5C26F2061E60 1 235535CC45D4816C7686C5A402A230B35A62DDE82B4A652E384FD31912C4E4BB 1 0C94B61595FCAEFC892BB98A27D524C97ED0B7ED1CC49AD6F178A59D4199C9A4 1 172482285606DEE8500FC8A13E6A35EC071F8B84F0EB4CD3DD091C0B4CD30E5E 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000\",\"timestamp\":1234}"; + + #[test] + fn json_rev_list_can_be_deserialized() { + let des = serde_json::from_str::(REVOCATION_LIST).unwrap(); + let expected_state = bitvec![1;4]; + println!("des state {:?}", des.state()); + println!("exp state {:?}", expected_state); + assert_eq!(des.state(), &expected_state); + } + + #[test] + fn test_revocation_list_roundtrip_serde() { + let state = bitvec![1;4]; + let accum = Accumulator::new().expect("Should be able to create Accumulator"); + let list = RevocationList::new("MOCK:uri: reg", state, accum.into(), 1234u64).unwrap(); + let ser = serde_json::to_string(&list).unwrap(); + let des = serde_json::from_str::(&ser).unwrap(); + let ser2 = serde_json::to_string(&des).unwrap(); + let list_des = serde_json::from_str::(&ser2).unwrap(); + assert_eq!(list, list_des) + } +} diff --git a/anoncreds/src/ffi/revocation.rs b/anoncreds/src/ffi/revocation.rs index 476e4240..db304ced 100644 --- a/anoncreds/src/ffi/revocation.rs +++ b/anoncreds/src/ffi/revocation.rs @@ -16,7 +16,7 @@ use crate::services::{ prover::create_or_update_revocation_state, tails::{TailsFileReader, TailsFileWriter}, types::{ - CredentialRevocationState, IssuanceType, RegistryType, RevocationRegistry, + CredentialRevocationState, IssuanceType, RegistryType, RevocationList, RevocationRegistry, RevocationRegistryDefinition, RevocationRegistryDefinitionPrivate, RevocationRegistryDelta, }, }; @@ -239,19 +239,23 @@ impl_anoncreds_object_from_json!( anoncreds_revocation_registry_delta_from_json ); +impl_anoncreds_object!(RevocationList, "RevocationList"); +impl_anoncreds_object_from_json!(RevocationList, anoncreds_revocation_list_from_json); + #[no_mangle] pub extern "C" fn anoncreds_create_or_update_revocation_state( rev_reg_def: ObjectHandle, - rev_reg_delta: ObjectHandle, + rev_reg_list: ObjectHandle, rev_reg_index: i64, - timestamp: i64, tails_path: FfiStr, rev_state: ObjectHandle, + old_rev_reg_list: ObjectHandle, rev_state_p: *mut ObjectHandle, ) -> ErrorCode { catch_error(|| { check_useful_c_ptr!(rev_state_p); let prev_rev_state = rev_state.opt_load()?; + let prev_rev_reg_list = old_rev_reg_list.opt_load()?; let tails_reader = TailsFileReader::new_tails_reader( tails_path .as_opt_str() @@ -260,17 +264,18 @@ pub extern "C" fn anoncreds_create_or_update_revocation_state( let rev_state = create_or_update_revocation_state( tails_reader, rev_reg_def.load()?.cast_ref()?, - rev_reg_delta.load()?.cast_ref()?, + rev_reg_list.load()?.cast_ref()?, rev_reg_index .try_into() .map_err(|_| err_msg!("Invalid credential revocation index"))?, - timestamp - .try_into() - .map_err(|_| err_msg!("Invalid timestamp"))?, prev_rev_state .as_ref() .map(AnonCredsObject::cast_ref) .transpose()?, + prev_rev_reg_list + .as_ref() + .map(AnonCredsObject::cast_ref) + .transpose()?, )?; let rev_state = ObjectHandle::create(rev_state)?; unsafe { *rev_state_p = rev_state }; diff --git a/anoncreds/src/services/prover.rs b/anoncreds/src/services/prover.rs index eb4b15b5..fb36dd89 100644 --- a/anoncreds/src/services/prover.rs +++ b/anoncreds/src/services/prover.rs @@ -1,4 +1,9 @@ -use std::collections::{HashMap, HashSet}; +use bitvec::bitvec; +use std::{ + collections::{HashMap, HashSet}, + convert::TryFrom, + ops::BitXor, +}; use super::types::*; use crate::data_types::anoncreds::{ @@ -9,14 +14,15 @@ use crate::data_types::anoncreds::{ AttributeValue, Identifier, RequestedProof, RevealedAttributeGroupInfo, RevealedAttributeInfo, SubProofReferent, }, + rev_reg::RevocationList, schema::SchemaId, }; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::services::helpers::*; use crate::ursa::cl::{ issuer::Issuer as CryptoIssuer, prover::Prover as CryptoProver, - verifier::Verifier as CryptoVerifier, CredentialPublicKey, - RevocationRegistry as CryptoRevocationRegistry, SubProofRequest, Witness, + verifier::Verifier as CryptoVerifier, CredentialPublicKey, RevocationRegistryDelta, + SubProofRequest, Witness, }; use indy_utils::Validatable; @@ -252,52 +258,101 @@ pub fn create_presentation( pub fn create_or_update_revocation_state( tails_reader: TailsReader, revoc_reg_def: &RevocationRegistryDefinition, - rev_reg_delta: &RevocationRegistryDelta, + rev_reg_list: &RevocationList, rev_reg_idx: u32, - timestamp: u64, - rev_state: Option<&CredentialRevocationState>, + rev_state: Option<&CredentialRevocationState>, // for witness update + old_rev_reg_list: Option<&RevocationList>, // for witness update ) -> Result { trace!( "create_or_update_revocation_state >>> , tails_reader: {:?}, revoc_reg_def: {:?}, \ -rev_reg_delta: {:?}, rev_reg_idx: {}, timestamp: {:?}, rev_state: {:?}", + rev_reg_list: {:?}, rev_reg_idx: {}, rev_state: {:?}, old_rev_reg_list {:?}", tails_reader, revoc_reg_def, - rev_reg_delta, + rev_reg_list, rev_reg_idx, - timestamp, - rev_state + rev_state, + old_rev_reg_list, ); let RevocationRegistryDefinition::RevocationRegistryDefinitionV1(revoc_reg_def) = revoc_reg_def; - let RevocationRegistryDelta::RevocationRegistryDeltaV1(rev_reg_delta) = rev_reg_delta; - - let witness = match rev_state { - None => Witness::new( - rev_reg_idx, - revoc_reg_def.value.max_cred_num, - revoc_reg_def.value.issuance_type.to_bool(), - &rev_reg_delta.value, - &tails_reader, - )?, - Some(source_rev_state) => { + let mut issued = HashSet::::new(); + let mut revoked = HashSet::::new(); + let witness = + if let (Some(source_rev_state), Some(source_rev_list)) = (rev_state, old_rev_reg_list) { + _create_index_deltas( + rev_reg_list.state_owned().bitxor(source_rev_list.state()), + rev_reg_list.state(), + &mut issued, + &mut revoked, + ); + + let rev_reg_delta = RevocationRegistryDelta::from_parts( + Some(&source_rev_list.into()), + &rev_reg_list.into(), + &issued, + &revoked, + ); let mut witness = source_rev_state.witness.clone(); witness.update( rev_reg_idx, revoc_reg_def.value.max_cred_num, - &rev_reg_delta.value, + &rev_reg_delta, &tails_reader, )?; witness - } - }; + } else { + let list_size = usize::try_from(revoc_reg_def.value.max_cred_num) + .map_err(|e| Error::from_msg(crate::ErrorKind::InvalidState, e.to_string()))?; + let list = match revoc_reg_def.value.issuance_type { + // All cred are revoked by default + IssuanceType::ISSUANCE_ON_DEMAND => { + bitvec![1; list_size] + } + IssuanceType::ISSUANCE_BY_DEFAULT => { + bitvec![0; list_size] + } + }; + _create_index_deltas( + rev_reg_list.state_owned().bitxor(list), + rev_reg_list.state(), + &mut issued, + &mut revoked, + ); + let rev_reg_delta = + RevocationRegistryDelta::from_parts(None, &rev_reg_list.into(), &issued, &revoked); + Witness::new( + rev_reg_idx, + revoc_reg_def.value.max_cred_num, + revoc_reg_def.value.issuance_type.to_bool(), + &rev_reg_delta, + &tails_reader, + )? + }; Ok(CredentialRevocationState { witness, - rev_reg: CryptoRevocationRegistry::from(rev_reg_delta.value.clone()), - timestamp, + rev_reg: rev_reg_list.into(), + timestamp: rev_reg_list.timestamp(), }) } +fn _create_index_deltas( + delta: bitvec::vec::BitVec, + list: &bitvec::vec::BitVec, + issued: &mut HashSet, + revoked: &mut HashSet, +) { + for i in delta.iter_ones() { + if list[i] == true { + // true means cred has been revoked + revoked.insert(i as u32); + } else { + // false means cred has not been + issued.insert(i as u32); + } + } +} + fn prepare_credential_for_proving( requested_attributes: HashSet<(String, bool)>, requested_predicates: HashSet, diff --git a/anoncreds/src/services/types.rs b/anoncreds/src/services/types.rs index 6e847ba0..699dca94 100644 --- a/anoncreds/src/services/types.rs +++ b/anoncreds/src/services/types.rs @@ -12,7 +12,7 @@ pub use crate::data_types::anoncreds::{ master_secret::MasterSecret, pres_request::PresentationRequest, presentation::Presentation, - rev_reg::{RevocationRegistry, RevocationRegistryDelta}, + rev_reg::{RevocationList, RevocationRegistry, RevocationRegistryDelta}, rev_reg_def::{ IssuanceType, RegistryType, RevocationRegistryDefinition, RevocationRegistryDefinitionPrivate, @@ -203,9 +203,9 @@ pub(crate) struct ProvingCredentialKey { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CredentialRevocationState { - pub(crate) witness: Witness, - pub(crate) rev_reg: CryptoRevocationRegistry, - pub(crate) timestamp: u64, + pub witness: Witness, + pub rev_reg: CryptoRevocationRegistry, + pub timestamp: u64, } impl Validatable for CredentialRevocationState { diff --git a/anoncreds/src/services/verifier.rs b/anoncreds/src/services/verifier.rs index 0d59c234..0ff64169 100644 --- a/anoncreds/src/services/verifier.rs +++ b/anoncreds/src/services/verifier.rs @@ -69,6 +69,8 @@ pub fn verify_presentation( &received_self_attested_attrs, )?; + // makes sure the for revocable request or attribute, + // there is a timestamp in the `Identifier` compare_timestamps_from_proof_and_request( pres_req, &received_revealed_attrs, @@ -111,6 +113,7 @@ pub fn verify_presentation( )); } + // Revocation registry definition id is the same as the rev reg id let rev_reg_def_id = RevocationRegistryDefinitionId::new(rev_reg_id.clone())?; let rev_reg_def = Some( rev_reg_defs @@ -302,6 +305,8 @@ fn compare_attr_from_proof_and_request( Ok(()) } +// This does not actually compare the non_revoke interval +// see `validate_timestamp` function comments fn compare_timestamps_from_proof_and_request( pres_req: &PresentationRequestPayload, received_revealed_attrs: &HashMap, @@ -352,6 +357,19 @@ fn compare_timestamps_from_proof_and_request( Ok(()) } +// This validates that a timestamp is given if either: +// - the `global_interval` rev requirement +// - the `local_interval` rev requirement +// from the PresentationRequest are satisfied. +// +// If either the attribute nor the request has a revocation internal +// i.e. they are non-revocable, then `OK` is returned directly. +// +// Otherwise the Identifier for the referent (attribute) has to have a timestamp, +// which was added by the prover when creating `PresentCredentials`, +// an arg for `create_presentation`. +// +// TODO: this timestamp should be compared with the provided interval fn validate_timestamp( received_: &HashMap, referent: &str, diff --git a/anoncreds/tests/anoncreds_demos.rs b/anoncreds/tests/anoncreds_demos.rs index d1bbdce9..759f6b0c 100644 --- a/anoncreds/tests/anoncreds_demos.rs +++ b/anoncreds/tests/anoncreds_demos.rs @@ -1,11 +1,22 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use anoncreds::{ - data_types::anoncreds::{cred_def::CredentialDefinitionId, schema::SchemaId}, + data_types::anoncreds::{ + cred_def::CredentialDefinitionId, presentation::Presentation, + rev_reg::RevocationRegistryId, rev_reg_def::RevocationRegistryDefinitionId, + schema::SchemaId, + }, issuer, prover, - types::{CredentialDefinitionConfig, MakeCredentialValues, PresentCredentials, SignatureType}, + tails::{TailsFileReader, TailsFileWriter}, + types::{ + CredentialDefinition, CredentialDefinitionConfig, CredentialRevocationConfig, + CredentialRevocationState, IssuanceType, MakeCredentialValues, PresentCredentials, + PresentationRequest, RegistryType, RevocationList, RevocationRegistry, + RevocationRegistryDefinition, Schema, SignatureType, + }, verifier, }; +use bitvec::bitvec; use serde_json::json; @@ -17,6 +28,9 @@ pub static SCHEMA_ID: &str = "mock:uri"; pub static CRED_DEF_ID: &str = "mock:uri"; pub const GVT_SCHEMA_NAME: &str = "gvt"; pub const GVT_SCHEMA_ATTRIBUTES: &[&str; 4] = &["name", "age", "sex", "height"]; +pub static REV_REG_ID: &str = "mock:uri:revregid"; +pub static REV_IDX: u32 = 89; +pub static MAX_CRED_NUM: u32 = 150; #[test] fn anoncreds_works_for_single_issuer_single_prover() { @@ -212,6 +226,318 @@ fn anoncreds_works_for_single_issuer_single_prover() { assert!(valid); } +#[test] +fn anoncreds_with_revocation_works_for_single_issuer_single_prover() { + env_logger::init(); + // Create Issuer pseudo wallet + let mut issuer_wallet = IssuerWallet::default(); + + // Create Prover pseudo wallet and master secret + let mut prover_wallet = ProverWallet::default(); + + // Issuer creates Schema - would be published to the ledger + let gvt_schema = + issuer::create_schema(GVT_SCHEMA_NAME, "1.0", GVT_SCHEMA_ATTRIBUTES[..].into()) + .expect("Error creating gvt schema for issuer"); + + // Issuer creates Credential Definition + let (cred_def_pub, cred_def_priv, cred_def_correctness) = issuer::create_credential_definition( + SCHEMA_ID, + &gvt_schema, + "tag", + SignatureType::CL, + CredentialDefinitionConfig { + support_revocation: true, + }, + ) + .expect("Error creating gvt credential definition"); + + // This will create a tails file locally in the .tmp dir + let tf_path = "../.tmp"; + let mut tf = TailsFileWriter::new(Some(tf_path.to_owned())); + let (rev_reg_def_pub, rev_reg_def_priv, rev_reg, _) = issuer::create_revocation_registry( + &cred_def_pub, + CRED_DEF_ID, + "some_tag", + RegistryType::CL_ACCUM, + IssuanceType::ISSUANCE_BY_DEFAULT, + MAX_CRED_NUM, + &mut tf, + ) + .unwrap(); + + issuer_wallet + .cred_defs + .push(utils::anoncreds::StoredCredDef { + public: cred_def_pub, + private: cred_def_priv, + key_proof: cred_def_correctness, + }); + + // Public part would be published to the ledger + let gvt_cred_def = &issuer_wallet.cred_defs[0].public; + + // Issuer creates a Credential Offer + let cred_offer = issuer::create_credential_offer( + SCHEMA_ID, + CRED_DEF_ID, + &issuer_wallet.cred_defs[0].key_proof, + ) + .expect("Error creating credential offer"); + + // Prover creates a Credential Request + let (cred_request, cred_request_metadata) = prover::create_credential_request( + &prover_wallet.did, + &*gvt_cred_def, + &prover_wallet.master_secret, + "default", + &cred_offer, + ) + .expect("Error creating credential request"); + + // Issuer creates a credential + let mut cred_values = MakeCredentialValues::default(); + cred_values + .add_raw("sex", "male") + .expect("Error encoding attribute"); + cred_values + .add_raw("name", "Alex") + .expect("Error encoding attribute"); + cred_values + .add_raw("height", "175") + .expect("Error encoding attribute"); + cred_values + .add_raw("age", "28") + .expect("Error encoding attribute"); + + let rev_reg_id = RevocationRegistryId::new_unchecked(REV_REG_ID); + + // Get the location of the tails_file so it can be read + let location = match rev_reg_def_pub.clone() { + RevocationRegistryDefinition::RevocationRegistryDefinitionV1(value) => { + value.value.tails_location + } + }; + let tr = TailsFileReader::new_tails_reader(location.as_str()); + + // The Prover's index in the revocation list is REV_IDX + let registry_used = HashSet::from([REV_IDX]); + + // TODO: Here Delta is not needed but is it used elsewhere? + let (issue_cred, cred_rev_reg, _) = issuer::create_credential( + &*gvt_cred_def, + &issuer_wallet.cred_defs[0].private, + &cred_offer, + &cred_request, + cred_values.into(), + Some(rev_reg_id.clone()), + Some(CredentialRevocationConfig { + reg_def: &rev_reg_def_pub, + reg_def_private: &rev_reg_def_priv, + registry: &rev_reg, + registry_idx: REV_IDX, + registry_used: ®istry_used, + tails_reader: tr, + }), + ) + .expect("Error creating credential"); + let cred_rev_reg = cred_rev_reg.unwrap(); + + // Prover receives the credential and processes it + let mut recv_cred = issue_cred; + prover::process_credential( + &mut recv_cred, + &cred_request_metadata, + &prover_wallet.master_secret, + &*gvt_cred_def, + Some(&rev_reg_def_pub), + ) + .expect("Error processing credential"); + prover_wallet.credentials.push(recv_cred); + + // Verifier creates a presentation request + let nonce = verifier::generate_nonce().expect("Error generating presentation request nonce"); + + // There are fields for + // - global non_revoked - i.e. the PresentationRequest level + // - local non_revoked - i.e. Each Request Attributes (AttributeInfo) and Request Predicate (PredicateInfo) has a field for NonRevoked. + let pres_request = serde_json::from_value(json!({ + "nonce": nonce, + "name":"pres_req_1", + "version":"0.1", + "requested_attributes":{ + "attr1_referent":{ + "name":"name" + }, + "attr2_referent":{ + "name":"sex" + }, + "attr3_referent":{"name":"phone"}, + "attr4_referent":{ + "names": ["name", "height"] + } + }, + "requested_predicates":{ + "predicate1_referent":{"name":"age","p_type":">=","p_value":18} + }, + "non_revoked": {"from": 10, "to": 200} + })) + .expect("Error creating proof request"); + + // Prover: here we deliberately do not put in the same timestamp as the global non_revoked time interval, + // this shows that it is not used + let prover_timestamp = 1234u64; + let rev_reg = match cred_rev_reg.clone() { + RevocationRegistry::RevocationRegistryV1(r) => r.value, + }; + let rev_state = CredentialRevocationState { + timestamp: prover_timestamp, + rev_reg: rev_reg.clone(), + witness: prover_wallet.credentials[0] + .try_clone() + .unwrap() + .witness + .unwrap(), + }; + + let mut schemas = HashMap::new(); + let schema_id = SchemaId::new_unchecked(SCHEMA_ID); + schemas.insert(&schema_id, &gvt_schema); + + let mut cred_defs = HashMap::new(); + let cred_def_id = CredentialDefinitionId::new_unchecked(CRED_DEF_ID); + cred_defs.insert(&cred_def_id, &*gvt_cred_def); + + // Prover creates presentation + let presentation = _create_presentation( + &schemas, + &cred_defs, + &pres_request, + &prover_wallet, + Some(prover_timestamp), + Some(&rev_state), + ); + + // Verifier verifies presentation of not Revoked rev_state + // TODO: rev reg def id is the same as the rev reg id? + let rev_reg_def_id = RevocationRegistryDefinitionId::new_unchecked(REV_REG_ID); + let rev_reg_def_map = HashMap::from([(&rev_reg_def_id, &rev_reg_def_pub)]); + + // Create the map that contines the registries + let rev_timestamp_map = HashMap::from([(prover_timestamp, &cred_rev_reg)]); + let mut rev_reg_map = HashMap::from([(rev_reg_id.clone(), rev_timestamp_map.clone())]); + + let valid = verifier::verify_presentation( + &presentation, + &pres_request, + &schemas, + &cred_defs, + Some(&rev_reg_def_map), + Some(&rev_reg_map), + ) + .expect("Error verifying presentation"); + assert!(valid); + + // Issuer Revoke the holder's credentail + let tr = TailsFileReader::new_tails_reader(location.as_str()); + let (revoked_rev_reg, _) = + issuer::revoke_credential(&rev_reg_def_pub, &cred_rev_reg, REV_IDX, &tr).unwrap(); + + // Prover using the revoked list to update witness and create rev state, + let mut list = bitvec![0; MAX_CRED_NUM as usize ]; + let mut revoked_bit = list.get_mut(REV_IDX as usize).unwrap(); + *revoked_bit = true; + // revoked_bit is not a reference so can drop + drop(revoked_bit); + + let revoked_accum = match revoked_rev_reg.clone() { + RevocationRegistry::RevocationRegistryV1(v) => v.value, + }; + + let revocation_list = + RevocationList::new(REV_REG_ID, list, revoked_accum.into(), prover_timestamp).unwrap(); + let new_rev_state = prover::create_or_update_revocation_state( + tr, + &rev_reg_def_pub, + &revocation_list, + REV_IDX, + None, + None, + ) + .unwrap(); + + // lets say the proof is for a later time + // TODO: this has nothing to do with pres_request time at the moment + let new_prover_timestamp = prover_timestamp + 100; + + let presentation = _create_presentation( + &schemas, + &cred_defs, + &pres_request, + &prover_wallet, + Some(new_prover_timestamp), + Some(&new_rev_state), + ); + + // Add the updated revocation registyr to the map that contines the new registry of revoked state + let r = rev_reg_map.get_mut(&rev_reg_id).unwrap(); + r.insert(new_prover_timestamp, &revoked_rev_reg); + + let valid = verifier::verify_presentation( + &presentation, + &pres_request, + &schemas, + &cred_defs, + Some(&rev_reg_def_map), + Some(&rev_reg_map), + ) + .expect("Error verifying presentation"); + assert!(!valid); +} + +fn _create_presentation( + schemas: &HashMap<&SchemaId, &Schema>, + cred_defs: &HashMap<&CredentialDefinitionId, &CredentialDefinition>, + pres_request: &PresentationRequest, + prover_wallet: &ProverWallet, + rev_state_timestamp: Option, + rev_state: Option<&CredentialRevocationState>, +) -> Presentation { + let mut present = PresentCredentials::default(); + { + // Here we add credential with the timestamp of which the rev_state is updated to, + // also the rev_reg has to be provided for such a time. + // TODO: this timestamp is not verified by the `NonRevokedInterval`? + let mut cred1 = present.add_credential( + &prover_wallet.credentials[0], + rev_state_timestamp, + rev_state, + ); + cred1.add_requested_attribute("attr1_referent", true); + cred1.add_requested_attribute("attr2_referent", false); + cred1.add_requested_attribute("attr4_referent", true); + cred1.add_requested_predicate("predicate1_referent"); + } + + let mut self_attested = HashMap::new(); + let self_attested_phone = "8-800-300"; + self_attested.insert( + "attr3_referent".to_string(), + self_attested_phone.to_string(), + ); + + let presentation = prover::create_presentation( + pres_request, + present, + Some(self_attested), + &prover_wallet.master_secret, + schemas, + cred_defs, + ) + .expect("Error creating presentation"); + presentation +} + /* #[test] fn anoncreds_works_for_multiple_issuer_single_prover() {