use serde::{Deserialize, Serialize};
use crate::{Address, BlockHeight, Timestamp};
use crate::agreement::{AttachmentRef, PartyRef};
pub type ProviderId = [u8; 32];
pub type MembershipId = [u8; 32];
pub type ConsentId = [u8; 32];
pub type PrescriptionId = [u8; 32];
pub type PolicyId = [u8; 32];
pub type ProofId = [u8; 32];
pub type SubjectId = [u8; 32];
pub const PROVIDER_DOMAIN_SEP: &[u8] = b"SRC871-PROVIDER:";
pub const PROVIDER_COMMITMENT_SEP: &[u8] = b"SRC871-COMMITMENT:v1:";
pub const MEMBERSHIP_DOMAIN_SEP: &[u8] = b"SRC872-MEMBERSHIP:";
pub const MEMBERSHIP_COMMITMENT_SEP: &[u8] = b"SRC872-COMMITMENT:v1:";
pub const CONSENT_DOMAIN_SEP: &[u8] = b"SRC874-CONSENT:";
pub const CONSENT_COMMITMENT_SEP: &[u8] = b"SRC874-COMMITMENT:v1:";
pub const PRESCRIPTION_DOMAIN_SEP: &[u8] = b"SRC876-PRESCRIPTION:";
pub const PRESCRIPTION_COMMITMENT_SEP: &[u8] = b"SRC876-COMMITMENT:v1:";
pub const HEALTHCARE_PROOF_DOMAIN_SEP: &[u8] = b"SRC875-PROOF:";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum ProviderType {
Hospital = 0,
Physician = 1,
Specialist = 2,
Clinic = 3,
NursingFacility = 4,
HomeHealthAgency = 5,
Pharmacy = 6,
Laboratory = 7,
ImagingCenter = 8,
MentalHealthProvider = 9,
Dentist = 10,
Optometrist = 11,
PhysicalTherapist = 12,
Chiropractor = 13,
AmbulanceEms = 14,
Hospice = 15,
UrgentCare = 16,
Telemedicine = 17,
HealthInsurer = 30,
Pbm = 31,
Tpa = 32,
Medicare = 33,
Medicaid = 34,
HealthPlan = 35,
Hmo = 36,
Ppo = 37,
ProfessionalAssociation = 50,
TradeUnion = 51,
GymFitness = 52,
MembershipClub = 53,
AlumniAssociation = 54,
ReligiousOrganization = 55,
Cooperative = 56,
Other = 255,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum ProviderStatus {
Active = 0,
Pending = 1,
Suspended = 2,
Revoked = 3,
Expired = 4,
UnderReview = 5,
Inactive = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum HealthcareIssuerClass {
GovernmentHealthAgency = 0,
StateMedicalBoard = 1,
Dea = 2,
StatePharmacyBoard = 3,
AccreditationBody = 4,
HospitalSystem = 10,
InsuranceCompany = 11,
MedicalPractice = 12,
PharmacyChain = 13,
LaboratoryNetwork = 14,
Hie = 15,
Clearinghouse = 16,
CredentialingOrg = 17,
Other = 255,
}
impl HealthcareIssuerClass {
pub fn is_official(&self) -> bool {
matches!(
self,
Self::GovernmentHealthAgency
| Self::StateMedicalBoard
| Self::Dea
| Self::StatePharmacyBoard
| Self::AccreditationBody
)
}
pub fn is_lowkey(&self) -> bool {
matches!(
self,
Self::HospitalSystem
| Self::InsuranceCompany
| Self::MedicalPractice
| Self::PharmacyChain
| Self::LaboratoryNetwork
| Self::Hie
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum NetworkStatus {
InNetwork = 0,
OutOfNetwork = 1,
Preferred = 2,
Tier1 = 3,
Tier2 = 4,
Participating = 5,
NonParticipating = 6,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProviderProfile {
pub provider_id: ProviderId,
pub provider_commitment: [u8; 32],
pub provider_type: ProviderType,
pub jurisdiction_code: String,
pub public_reference: Option<String>,
pub specialties_commitment: Option<[u8; 32]>,
pub credentials_commitment: Option<[u8; 32]>,
pub policy_id: PolicyId,
pub issuer_class: HealthcareIssuerClass,
pub issuer_address: Address,
pub status: ProviderStatus,
pub created_at: Timestamp,
pub updated_at: Timestamp,
pub registered_at_height: BlockHeight,
pub network_affiliations: Vec<ProviderId>,
pub attachments: Vec<AttachmentRef>,
}
impl ProviderProfile {
pub fn generate_id(
issuer: &Address,
provider_commitment: &[u8; 32],
provider_type: ProviderType,
jurisdiction: &str,
nonce: &[u8; 32],
) -> ProviderId {
let mut hasher = blake3::Hasher::new();
hasher.update(PROVIDER_DOMAIN_SEP);
hasher.update(b":v1:");
hasher.update(issuer.as_ref());
hasher.update(provider_commitment);
hasher.update(&[provider_type as u8]);
hasher.update(jurisdiction.as_bytes());
hasher.update(nonce);
*hasher.finalize().as_bytes()
}
pub fn generate_commitment(
npi_commitment: Option<&[u8; 32]>,
name_commitment: &[u8; 32],
address_commitment: Option<&[u8; 32]>,
) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(PROVIDER_COMMITMENT_SEP);
if let Some(npi) = npi_commitment {
hasher.update(npi);
}
hasher.update(name_commitment);
if let Some(addr) = address_commitment {
hasher.update(addr);
}
*hasher.finalize().as_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum MembershipType {
IndividualHealth = 0,
FamilyHealth = 1,
EmployerHealth = 2,
MedicarePartA = 3,
MedicarePartB = 4,
MedicareAdvantage = 5,
MedicarePartD = 6,
MedicaidCoverage = 7,
Chip = 8,
Dental = 9,
Vision = 10,
Pharmacy = 11,
MentalHealth = 12,
HsaFsa = 13,
ProfessionalLicense = 20,
BoardCertification = 21,
AssociationMembership = 22,
UnionMembership = 23,
Accreditation = 24,
GymMembership = 40,
ClubMembership = 41,
Subscription = 42,
LoyaltyProgram = 43,
AlumniStatus = 44,
Other = 255,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum MembershipStatus {
Active = 0,
Pending = 1,
Suspended = 2,
Cancelled = 3,
Expired = 4,
Lapsed = 5,
Terminated = 6,
GracePeriod = 7,
Cobra = 8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum CoverageTier {
EmployeeOnly = 0,
EmployeeSpouse = 1,
EmployeeChildren = 2,
Family = 3,
Individual = 4,
SelfOnly = 5,
SelfPlusOne = 6,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MembershipRecord {
pub membership_id: MembershipId,
pub member_address: Address,
pub provider_id: ProviderId,
pub membership_type: MembershipType,
pub membership_commitment: [u8; 32],
pub member_ref: PartyRef,
pub member_nullifier: [u8; 32],
pub coverage_tier: Option<CoverageTier>,
pub group_commitment: Option<[u8; 32]>,
pub effective_from: Timestamp,
pub expiry: Option<Timestamp>,
pub issuer_address: Address,
pub issuer_class: HealthcareIssuerClass,
pub policy_id: PolicyId,
pub revocation_ref: Option<[u8; 32]>,
pub status: MembershipStatus,
pub created_at: Timestamp,
pub updated_at: Timestamp,
pub issued_at_height: BlockHeight,
pub prior_membership_id: Option<MembershipId>,
pub dependents: Vec<[u8; 32]>,
pub attachments: Vec<AttachmentRef>,
}
impl MembershipRecord {
pub fn generate_id(
provider_id: &ProviderId,
membership_type: MembershipType,
member_nullifier: &[u8; 32],
nonce: &[u8; 32],
) -> MembershipId {
let mut hasher = blake3::Hasher::new();
hasher.update(MEMBERSHIP_DOMAIN_SEP);
hasher.update(b":v1:");
hasher.update(provider_id);
hasher.update(&[membership_type as u8]);
hasher.update(member_nullifier);
hasher.update(nonce);
*hasher.finalize().as_bytes()
}
pub fn generate_commitment(
member_id_commitment: &[u8; 32],
benefits_commitment: Option<&[u8; 32]>,
copay_commitment: Option<&[u8; 32]>,
) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(MEMBERSHIP_COMMITMENT_SEP);
hasher.update(member_id_commitment);
if let Some(bc) = benefits_commitment {
hasher.update(bc);
}
if let Some(cc) = copay_commitment {
hasher.update(cc);
}
*hasher.finalize().as_bytes()
}
pub fn is_active(&self, current_time: Timestamp) -> bool {
if !matches!(
self.status,
MembershipStatus::Active | MembershipStatus::GracePeriod | MembershipStatus::Cobra
) {
return false;
}
if current_time < self.effective_from {
return false;
}
if let Some(exp) = self.expiry {
if current_time >= exp {
return false;
}
}
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum ConsentType {
HipaaAuthorization = 0,
NppAcknowledgment = 1,
ConsentToTreat = 2,
AssignmentOfBenefits = 3,
RecordsRelease = 4,
ResearchConsent = 5,
TelehealthConsent = 6,
PsychotherapyNotes = 7,
SubstanceAbuseRecords = 8,
HivStiDisclosure = 9,
GeneticInformation = 10,
GdprConsent = 20,
MarketingConsent = 21,
DataSharingConsent = 22,
AnalyticsConsent = 23,
ThirdPartySharing = 24,
TermsOfService = 40,
PrivacyPolicy = 41,
CodeOfConduct = 42,
BackgroundCheck = 43,
Other = 255,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum ConsentStatus {
Granted = 0,
Pending = 1,
Revoked = 2,
Expired = 3,
Denied = 4,
Superseded = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum DisclosureScope {
AllRecords = 0,
DateRange = 1,
SpecificEncounter = 2,
SpecificCondition = 3,
TreatmentOnly = 4,
PaymentOnly = 5,
HealthcareOperations = 6,
MinimumNecessary = 7,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConsentEnvelope {
pub consent_id: ConsentId,
pub subject_address: Address,
pub consent_type: ConsentType,
pub consent_commitment: [u8; 32],
pub subject_ref: PartyRef,
pub subject_nullifier: [u8; 32],
pub recipient_ref: PartyRef,
pub purpose_commitment: [u8; 32],
pub scope: DisclosureScope,
pub scope_commitment: Option<[u8; 32]>,
pub effective_from: Timestamp,
pub expiry: Option<Timestamp>,
pub issuer_address: Address,
pub issuer_class: HealthcareIssuerClass,
pub policy_id: PolicyId,
pub revocation_ref: Option<[u8; 32]>,
pub status: ConsentStatus,
pub created_at: Timestamp,
pub updated_at: Timestamp,
pub recorded_at_height: BlockHeight,
pub supersedes: Option<ConsentId>,
pub attachments: Vec<AttachmentRef>,
}
impl ConsentEnvelope {
pub fn generate_id(
subject_nullifier: &[u8; 32],
consent_type: ConsentType,
recipient_ref: &PartyRef,
nonce: &[u8; 32],
) -> ConsentId {
let mut hasher = blake3::Hasher::new();
hasher.update(CONSENT_DOMAIN_SEP);
hasher.update(b":v1:");
hasher.update(subject_nullifier);
hasher.update(&[consent_type as u8]);
hasher.update(&recipient_ref.as_hash());
hasher.update(nonce);
*hasher.finalize().as_bytes()
}
pub fn generate_commitment(
consent_text_hash: &[u8; 32],
purpose_commitment: &[u8; 32],
restrictions_hash: Option<&[u8; 32]>,
) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(CONSENT_COMMITMENT_SEP);
hasher.update(consent_text_hash);
hasher.update(purpose_commitment);
if let Some(rh) = restrictions_hash {
hasher.update(rh);
}
*hasher.finalize().as_bytes()
}
pub fn is_valid(&self, current_time: Timestamp) -> bool {
if !matches!(self.status, ConsentStatus::Granted) {
return false;
}
if current_time < self.effective_from {
return false;
}
if let Some(exp) = self.expiry {
if current_time >= exp {
return false;
}
}
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum PrescriptionType {
ScheduleI = 0,
ScheduleII = 1,
ScheduleIII = 2,
ScheduleIV = 3,
ScheduleV = 4,
StandardPrescription = 20,
SpecialtyMedication = 21,
CompoundMedication = 22,
Biologic = 23,
Biosimilar = 24,
Generic = 25,
BrandName = 26,
Dme = 40,
MedicalSupplies = 41,
Prosthetics = 42,
Orthotics = 43,
Otc = 50,
NutritionalSupplement = 51,
Optical = 52,
Other = 255,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum PrescriptionStatus {
Active = 0,
Pending = 1,
OnHold = 2,
Filled = 3,
PartiallyFilled = 4,
Expired = 5,
Cancelled = 6,
Superseded = 7,
TransferRequested = 8,
Denied = 9,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Prescription {
pub prescription_id: PrescriptionId,
pub patient_address: Address,
pub prescription_type: PrescriptionType,
pub prescription_commitment: [u8; 32],
pub patient_ref: PartyRef,
pub patient_nullifier: [u8; 32],
pub prescriber_ref: PartyRef,
pub prescriber_provider_id: ProviderId,
pub pharmacy_ref: Option<PartyRef>,
pub medication_commitment: [u8; 32],
pub quantity_commitment: [u8; 32],
pub days_supply_commitment: Option<[u8; 32]>,
pub refills_authorized: u8,
pub refills_remaining: u8,
pub is_controlled: bool,
pub date_written: Timestamp,
pub effective_from: Option<Timestamp>,
pub expiry: Timestamp,
pub issuer_address: Address,
pub issuer_class: HealthcareIssuerClass,
pub policy_id: PolicyId,
pub revocation_ref: Option<[u8; 32]>,
pub status: PrescriptionStatus,
pub created_at: Timestamp,
pub updated_at: Timestamp,
pub recorded_at_height: BlockHeight,
pub supersedes: Option<PrescriptionId>,
pub fill_history: Vec<[u8; 32]>,
pub attachments: Vec<AttachmentRef>,
}
impl Prescription {
pub fn generate_id(
prescriber_provider_id: &ProviderId,
patient_nullifier: &[u8; 32],
medication_commitment: &[u8; 32],
date_written: Timestamp,
nonce: &[u8; 32],
) -> PrescriptionId {
let mut hasher = blake3::Hasher::new();
hasher.update(PRESCRIPTION_DOMAIN_SEP);
hasher.update(b":v1:");
hasher.update(prescriber_provider_id);
hasher.update(patient_nullifier);
hasher.update(medication_commitment);
hasher.update(&date_written.to_le_bytes());
hasher.update(nonce);
*hasher.finalize().as_bytes()
}
pub fn generate_commitment(
medication_commitment: &[u8; 32],
dosage_commitment: &[u8; 32],
instructions_hash: Option<&[u8; 32]>,
) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(PRESCRIPTION_COMMITMENT_SEP);
hasher.update(medication_commitment);
hasher.update(dosage_commitment);
if let Some(ih) = instructions_hash {
hasher.update(ih);
}
*hasher.finalize().as_bytes()
}
pub fn is_valid(&self, current_time: Timestamp) -> bool {
if !matches!(
self.status,
PrescriptionStatus::Active | PrescriptionStatus::PartiallyFilled
) {
return false;
}
if let Some(eff) = self.effective_from {
if current_time < eff {
return false;
}
}
if current_time >= self.expiry {
return false;
}
true
}
pub fn can_transfer(&self) -> bool {
!self.is_controlled
&& matches!(self.status, PrescriptionStatus::Active)
&& self.refills_remaining > 0
}
pub fn has_refills(&self) -> bool {
self.refills_remaining > 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum HealthcareProofProfile {
MembershipActive = 0,
ProviderInNetwork = 1,
ConsentExists = 2,
ConsentValid = 3,
PrescriptionValid = 4,
PrescriptionHasRefills = 5,
CoverageTier = 6,
BenefitEligible = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum HealthcareProofType {
Mock = 0,
Groth16 = 1,
Plonk = 2,
ThresholdSignature = 3,
MerkleInclusion = 4,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HealthcareProofEnvelope {
pub proof_id: ProofId,
pub profile: HealthcareProofProfile,
pub profile_id: String,
pub policy_ids: Vec<PolicyId>,
pub public_inputs: Vec<u8>,
pub proof_data: Vec<u8>,
pub proof_type: HealthcareProofType,
pub subject_nullifier: [u8; 32],
pub generated_at: Timestamp,
pub expires_at: Timestamp,
}
impl HealthcareProofEnvelope {
pub fn generate_id(
profile: HealthcareProofProfile,
subject_nullifier: &[u8; 32],
policy_ids: &[PolicyId],
nonce: &[u8; 32],
) -> ProofId {
let mut hasher = blake3::Hasher::new();
hasher.update(HEALTHCARE_PROOF_DOMAIN_SEP);
hasher.update(b":v1:");
hasher.update(&[profile as u8]);
hasher.update(subject_nullifier);
for policy_id in policy_ids {
hasher.update(policy_id);
}
hasher.update(nonce);
*hasher.finalize().as_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum HealthcareOperation {
RegisterProvider = 0,
UpdateProvider = 1,
SuspendProvider = 2,
RevokeProvider = 3,
ReactivateProvider = 4,
AddNetworkAffiliation = 5,
RemoveNetworkAffiliation = 6,
IssueMembership = 10,
UpdateMembership = 11,
RenewMembership = 12,
SuspendMembership = 13,
TerminateMembership = 14,
ReinstateMembership = 15,
AddDependent = 16,
RemoveDependent = 17,
GrantConsent = 20,
UpdateConsent = 21,
RevokeConsent = 22,
SupersedeConsent = 23,
IssuePrescription = 30,
UpdatePrescription = 31,
FillPrescription = 32,
PartialFillPrescription = 33,
CancelPrescription = 34,
HoldPrescription = 35,
ReleaseHold = 36,
SubmitProof = 40,
VerifyProof = 41,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HealthcareTxData {
pub operation: HealthcareOperation,
pub data: Vec<u8>,
pub recipient: crate::Address,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum HealthcareEvent {
ProviderRegistered {
provider_id: ProviderId,
provider_type: ProviderType,
jurisdiction: String,
timestamp: Timestamp,
},
ProviderUpdated {
provider_id: ProviderId,
new_status: ProviderStatus,
timestamp: Timestamp,
},
NetworkAffiliationChanged {
provider_id: ProviderId,
plan_id: ProviderId,
added: bool,
timestamp: Timestamp,
},
MembershipIssued {
membership_id: MembershipId,
provider_id: ProviderId,
membership_type: MembershipType,
effective_from: Timestamp,
timestamp: Timestamp,
},
MembershipStatusUpdated {
membership_id: MembershipId,
new_status: MembershipStatus,
timestamp: Timestamp,
},
DependentChanged {
membership_id: MembershipId,
dependent_hash: [u8; 32],
added: bool,
timestamp: Timestamp,
},
ConsentGranted {
consent_id: ConsentId,
consent_type: ConsentType,
subject_nullifier: [u8; 32],
timestamp: Timestamp,
},
ConsentRevoked {
consent_id: ConsentId,
timestamp: Timestamp,
},
ConsentSuperseded {
old_consent_id: ConsentId,
new_consent_id: ConsentId,
timestamp: Timestamp,
},
PrescriptionIssued {
prescription_id: PrescriptionId,
prescription_type: PrescriptionType,
is_controlled: bool,
date_written: Timestamp,
expiry: Timestamp,
timestamp: Timestamp,
},
PrescriptionFilled {
prescription_id: PrescriptionId,
fill_commitment: [u8; 32],
refills_remaining: u8,
timestamp: Timestamp,
},
PrescriptionStatusUpdated {
prescription_id: PrescriptionId,
new_status: PrescriptionStatus,
timestamp: Timestamp,
},
HealthcareProofSubmitted {
proof_id: ProofId,
profile: HealthcareProofProfile,
timestamp: Timestamp,
},
HealthcareProofVerified {
proof_id: ProofId,
valid: bool,
timestamp: Timestamp,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_provider_id_generation() {
let issuer = Address::new([1u8; 20]);
let commitment = [2u8; 32];
let nonce = [3u8; 32];
let id = ProviderProfile::generate_id(
&issuer,
&commitment,
ProviderType::Hospital,
"US-CA",
&nonce,
);
assert_ne!(id, [0u8; 32]);
let id2 = ProviderProfile::generate_id(
&issuer,
&commitment,
ProviderType::Hospital,
"US-CA",
&nonce,
);
assert_eq!(id, id2);
let id3 = ProviderProfile::generate_id(
&issuer,
&commitment,
ProviderType::Pharmacy,
"US-CA",
&nonce,
);
assert_ne!(id, id3);
}
#[test]
fn test_provider_commitment() {
let name = [1u8; 32];
let commitment = ProviderProfile::generate_commitment(None, &name, None);
assert_ne!(commitment, [0u8; 32]);
}
#[test]
fn test_membership_active() {
let membership = MembershipRecord {
membership_id: [1u8; 32],
member_address: Address::new([0u8; 20]),
provider_id: [2u8; 32],
membership_type: MembershipType::IndividualHealth,
membership_commitment: [3u8; 32],
member_ref: PartyRef::Commitment([4u8; 32]),
member_nullifier: [5u8; 32],
coverage_tier: Some(CoverageTier::Individual),
group_commitment: None,
effective_from: 1000,
expiry: Some(2000),
issuer_address: Address::new([6u8; 20]),
issuer_class: HealthcareIssuerClass::InsuranceCompany,
policy_id: [7u8; 32],
revocation_ref: None,
status: MembershipStatus::Active,
created_at: 1000,
updated_at: 1000,
issued_at_height: 100,
prior_membership_id: None,
dependents: vec![],
attachments: vec![],
};
assert!(!membership.is_active(500)); assert!(membership.is_active(1500)); assert!(!membership.is_active(2500)); }
#[test]
fn test_consent_valid() {
let consent = ConsentEnvelope {
consent_id: [1u8; 32],
subject_address: Address::new([0u8; 20]),
consent_type: ConsentType::HipaaAuthorization,
consent_commitment: [2u8; 32],
subject_ref: PartyRef::Commitment([3u8; 32]),
subject_nullifier: [4u8; 32],
recipient_ref: PartyRef::Commitment([5u8; 32]),
purpose_commitment: [6u8; 32],
scope: DisclosureScope::TreatmentOnly,
scope_commitment: None,
effective_from: 1000,
expiry: Some(2000),
issuer_address: Address::new([7u8; 20]),
issuer_class: HealthcareIssuerClass::MedicalPractice,
policy_id: [8u8; 32],
revocation_ref: None,
status: ConsentStatus::Granted,
created_at: 1000,
updated_at: 1000,
recorded_at_height: 100,
supersedes: None,
attachments: vec![],
};
assert!(!consent.is_valid(500)); assert!(consent.is_valid(1500)); assert!(!consent.is_valid(2500)); }
#[test]
fn test_prescription_validity() {
let prescription = Prescription {
prescription_id: [1u8; 32],
patient_address: Address::new([0u8; 20]),
prescription_type: PrescriptionType::StandardPrescription,
prescription_commitment: [2u8; 32],
patient_ref: PartyRef::Commitment([3u8; 32]),
patient_nullifier: [4u8; 32],
prescriber_ref: PartyRef::Commitment([5u8; 32]),
prescriber_provider_id: [6u8; 32],
pharmacy_ref: None,
medication_commitment: [7u8; 32],
quantity_commitment: [8u8; 32],
days_supply_commitment: None,
refills_authorized: 3,
refills_remaining: 2,
is_controlled: false,
date_written: 900,
effective_from: Some(1000),
expiry: 2000,
issuer_address: Address::new([9u8; 20]),
issuer_class: HealthcareIssuerClass::MedicalPractice,
policy_id: [10u8; 32],
revocation_ref: None,
status: PrescriptionStatus::Active,
created_at: 900,
updated_at: 900,
recorded_at_height: 100,
supersedes: None,
fill_history: vec![],
attachments: vec![],
};
assert!(!prescription.is_valid(500)); assert!(prescription.is_valid(1500)); assert!(!prescription.is_valid(2500)); assert!(prescription.can_transfer());
assert!(prescription.has_refills());
}
#[test]
fn test_controlled_prescription_no_transfer() {
let prescription = Prescription {
prescription_id: [1u8; 32],
patient_address: Address::new([0u8; 20]),
prescription_type: PrescriptionType::ScheduleII,
prescription_commitment: [2u8; 32],
patient_ref: PartyRef::Commitment([3u8; 32]),
patient_nullifier: [4u8; 32],
prescriber_ref: PartyRef::Commitment([5u8; 32]),
prescriber_provider_id: [6u8; 32],
pharmacy_ref: None,
medication_commitment: [7u8; 32],
quantity_commitment: [8u8; 32],
days_supply_commitment: None,
refills_authorized: 0, refills_remaining: 0,
is_controlled: true,
date_written: 900,
effective_from: None,
expiry: 2000,
issuer_address: Address::new([9u8; 20]),
issuer_class: HealthcareIssuerClass::MedicalPractice,
policy_id: [10u8; 32],
revocation_ref: None,
status: PrescriptionStatus::Active,
created_at: 900,
updated_at: 900,
recorded_at_height: 100,
supersedes: None,
fill_history: vec![],
attachments: vec![],
};
assert!(!prescription.can_transfer());
assert!(!prescription.has_refills());
}
#[test]
fn test_healthcare_issuer_class() {
assert!(HealthcareIssuerClass::GovernmentHealthAgency.is_official());
assert!(HealthcareIssuerClass::Dea.is_official());
assert!(!HealthcareIssuerClass::InsuranceCompany.is_official());
assert!(HealthcareIssuerClass::InsuranceCompany.is_lowkey());
assert!(HealthcareIssuerClass::MedicalPractice.is_lowkey());
assert!(!HealthcareIssuerClass::GovernmentHealthAgency.is_lowkey());
}
#[test]
fn test_healthcare_proof_id() {
let nullifier = [1u8; 32];
let policies = vec![[2u8; 32], [3u8; 32]];
let nonce = [4u8; 32];
let id = HealthcareProofEnvelope::generate_id(
HealthcareProofProfile::MembershipActive,
&nullifier,
&policies,
&nonce,
);
assert_ne!(id, [0u8; 32]);
}
}