1use serde::{Deserialize, Serialize};
12use crate::{Address, BlockHeight, Timestamp};
13use crate::agreement::{AttachmentRef, PartyRef};
14
15pub type AssetId = [u8; 32];
21pub type TitleEventId = [u8; 32];
23pub type EncumbranceId = [u8; 32];
25pub type CoverageId = [u8; 32];
27pub type ClaimId = [u8; 32];
29pub type PolicyId = [u8; 32];
31pub type ProofId = [u8; 32];
33pub type SubjectId = [u8; 32];
35
36pub const ASSET_DOMAIN_SEP: &[u8] = b"SRC861-ASSET:";
41pub const ASSET_COMMITMENT_SEP: &[u8] = b"SRC861-COMMITMENT:v1:";
42pub const TITLE_DOMAIN_SEP: &[u8] = b"SRC862-TITLE:";
43pub const TITLE_COMMITMENT_SEP: &[u8] = b"SRC862-COMMITMENT:v1:";
44pub const ENCUMBRANCE_DOMAIN_SEP: &[u8] = b"SRC863-ENCUMBRANCE:";
45pub const ENCUMBRANCE_COMMITMENT_SEP: &[u8] = b"SRC863-COMMITMENT:v1:";
46pub const COVERAGE_DOMAIN_SEP: &[u8] = b"SRC864-COVERAGE:";
47pub const COVERAGE_COMMITMENT_SEP: &[u8] = b"SRC864-COMMITMENT:v1:";
48pub const CLAIM_DOMAIN_SEP: &[u8] = b"SRC865-CLAIM:";
49pub const CLAIM_COMMITMENT_SEP: &[u8] = b"SRC865-COMMITMENT:v1:";
50pub const PROPERTY_PROOF_DOMAIN_SEP: &[u8] = b"SRC866-PROOF:";
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
58#[repr(u8)]
59pub enum AssetType {
60 SingleFamilyResidence = 0,
63 MultiFamilyResidence = 1,
65 Condominium = 2,
67 Townhouse = 3,
69 Commercial = 4,
71 Industrial = 5,
73 VacantLand = 6,
75 Agricultural = 7,
77 MixedUse = 8,
79 ManufacturedHome = 9,
81
82 Automobile = 20,
85 Motorcycle = 21,
87 Watercraft = 22,
89 Aircraft = 23,
91 RecreationalVehicle = 24,
93 CommercialVehicle = 25,
95 HeavyEquipment = 26,
97
98 FineArt = 40,
101 Jewelry = 41,
103 Collectibles = 42,
105 Antiques = 43,
107 MusicalInstruments = 44,
109 Electronics = 45,
111 Furniture = 46,
113
114 Inventory = 60,
117 Equipment = 61,
119 Fixtures = 62,
121 IpBundle = 63,
123 Goodwill = 64,
125
126 Other = 255,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
132#[repr(u8)]
133pub enum AssetStatus {
134 Active = 0,
136 PendingTransfer = 1,
138 Encumbered = 2,
140 Seized = 3,
142 Destroyed = 4,
144 Merged = 5,
146 Subdivided = 6,
148 Deregistered = 7,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
154#[repr(u8)]
155pub enum PropertyIssuerClass {
156 LandRegistry = 0,
159 MotorVehicleDept = 1,
161 TaxAssessor = 2,
163 GovernmentAgency = 3,
165 AviationAuthority = 4,
167 MaritimeAuthority = 5,
169
170 TitleCompany = 10,
173 EscrowCompany = 11,
175 RealEstateAttorney = 12,
177 Appraiser = 13,
179 Surveyor = 14,
181 InsuranceCompany = 15,
183 MortgageLender = 16,
185 PropertyManager = 17,
187 Notary = 18,
189
190 Other = 255,
192}
193
194impl PropertyIssuerClass {
195 pub fn is_official(&self) -> bool {
197 matches!(
198 self,
199 Self::LandRegistry
200 | Self::MotorVehicleDept
201 | Self::TaxAssessor
202 | Self::GovernmentAgency
203 | Self::AviationAuthority
204 | Self::MaritimeAuthority
205 )
206 }
207
208 pub fn is_lowkey(&self) -> bool {
210 matches!(
211 self,
212 Self::TitleCompany
213 | Self::EscrowCompany
214 | Self::RealEstateAttorney
215 | Self::Appraiser
216 | Self::Surveyor
217 | Self::InsuranceCompany
218 | Self::MortgageLender
219 )
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225pub struct AssetAnchor {
226 pub asset_id: AssetId,
228 pub asset_commitment: [u8; 32],
230 pub asset_type: AssetType,
232 pub jurisdiction_code: String,
234 pub public_reference: Option<String>,
236 pub policy_id: PolicyId,
238 pub issuer_class: PropertyIssuerClass,
240 pub issuer_address: Address,
242 pub status: AssetStatus,
244 pub created_at: Timestamp,
246 pub updated_at: Timestamp,
248 pub anchored_at_height: BlockHeight,
250 pub related_assets: Vec<AssetId>,
252 pub attachments: Vec<AttachmentRef>,
254}
255
256impl AssetAnchor {
257 pub fn generate_id(
259 issuer: &Address,
260 asset_commitment: &[u8; 32],
261 asset_type: AssetType,
262 jurisdiction: &str,
263 nonce: &[u8; 32],
264 ) -> AssetId {
265 let mut hasher = blake3::Hasher::new();
266 hasher.update(ASSET_DOMAIN_SEP);
267 hasher.update(b":v1:");
268 hasher.update(issuer.as_ref());
269 hasher.update(asset_commitment);
270 hasher.update(&[asset_type as u8]);
271 hasher.update(jurisdiction.as_bytes());
272 hasher.update(nonce);
273 *hasher.finalize().as_bytes()
274 }
275
276 pub fn generate_commitment(
278 asset_details: &[u8],
279 location_commitment: Option<&[u8; 32]>,
280 identifier_commitment: Option<&[u8; 32]>,
281 ) -> [u8; 32] {
282 let mut hasher = blake3::Hasher::new();
283 hasher.update(ASSET_COMMITMENT_SEP);
284 hasher.update(asset_details);
285 if let Some(loc) = location_commitment {
286 hasher.update(loc);
287 }
288 if let Some(id) = identifier_commitment {
289 hasher.update(id);
290 }
291 *hasher.finalize().as_bytes()
292 }
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
301#[repr(u8)]
302pub enum TitleEventType {
303 InitialRegistration = 0,
306 TitleSearch = 1,
308 TitleExamination = 2,
310 TitleCommitment = 3,
312
313 DeedRecorded = 10,
316 GrantDeed = 11,
318 WarrantyDeed = 12,
320 QuitclaimDeed = 13,
322 TrustDeed = 14,
324 DeedOfGift = 15,
326 ExecutorsDeed = 16,
328 TaxDeed = 17,
330 SheriffsDeed = 18,
332
333 OwnershipTransfer = 20,
336 JointTenancyCreated = 21,
338 TenancyInCommon = 22,
340 TrustAssignment = 23,
342 Inheritance = 24,
344 DivorceTransfer = 25,
346
347 TitleCorrection = 30,
350 NameChange = 31,
352 LegalDescriptionUpdate = 32,
354 EasementRecorded = 33,
356 CovenantRecorded = 34,
358
359 TitleCleared = 40,
362 ForeclosureCompleted = 41,
364 TaxSaleCompleted = 42,
366 Condemnation = 43,
368
369 Other = 255,
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
375#[repr(u8)]
376pub enum TitleEventStatus {
377 Recorded = 0,
379 Pending = 1,
381 Superseded = 2,
383 Voided = 3,
385 Corrected = 4,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
391pub struct TitleEvent {
392 pub event_id: TitleEventId,
394 pub asset_id: AssetId,
396 pub event_type: TitleEventType,
398 pub event_commitment: [u8; 32],
400 pub grantor_ref: Option<PartyRef>,
402 pub grantee_ref: Option<PartyRef>,
404 pub issuer_address: Address,
406 pub issuer_class: PropertyIssuerClass,
408 pub effective_date: Timestamp,
410 pub recording_ref: Option<String>,
412 pub policy_id: PolicyId,
414 pub revocation_ref: Option<[u8; 32]>,
416 pub status: TitleEventStatus,
418 pub created_at: Timestamp,
420 pub recorded_at_height: BlockHeight,
422 pub supersedes: Option<TitleEventId>,
424 pub attachments: Vec<AttachmentRef>,
426}
427
428impl TitleEvent {
429 pub fn generate_id(
431 asset_id: &AssetId,
432 event_type: TitleEventType,
433 issuer: &Address,
434 nonce: &[u8; 32],
435 ) -> TitleEventId {
436 let mut hasher = blake3::Hasher::new();
437 hasher.update(TITLE_DOMAIN_SEP);
438 hasher.update(b":v1:");
439 hasher.update(asset_id);
440 hasher.update(&[event_type as u8]);
441 hasher.update(issuer.as_ref());
442 hasher.update(nonce);
443 *hasher.finalize().as_bytes()
444 }
445
446 pub fn generate_commitment(
448 deed_hash: &[u8; 32],
449 parties_commitment: &[u8; 32],
450 consideration_commitment: Option<&[u8; 32]>,
451 ) -> [u8; 32] {
452 let mut hasher = blake3::Hasher::new();
453 hasher.update(TITLE_COMMITMENT_SEP);
454 hasher.update(deed_hash);
455 hasher.update(parties_commitment);
456 if let Some(cc) = consideration_commitment {
457 hasher.update(cc);
458 }
459 *hasher.finalize().as_bytes()
460 }
461}
462
463#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
469#[repr(u8)]
470pub enum EncumbranceType {
471 FirstMortgage = 0,
474 SecondMortgage = 1,
476 Heloc = 2,
478 ReverseMortgage = 3,
480 ConstructionLoan = 4,
482 CommercialMortgage = 5,
484
485 MechanicsLien = 10,
488 TaxLien = 11,
490 JudgmentLien = 12,
492 HoaLien = 13,
494 ChildSupportLien = 14,
496 IrsLien = 15,
498 MunicipalLien = 16,
500 UccLien = 17,
502 VoluntaryLien = 18,
504 AttachmentLien = 19,
506
507 ResidentialLease = 30,
510 CommercialLease = 31,
512 GroundLease = 32,
514 Sublease = 33,
516
517 Easement = 40,
520 RightOfWay = 41,
522 RestrictiveCovenant = 42,
524 ConservationEasement = 43,
526 UtilityEasement = 44,
528
529 LisPendens = 50,
532 NoticeOfDefault = 51,
534 NoticeOfTrusteeSale = 52,
536
537 Other = 255,
539}
540
541#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
543#[repr(u8)]
544pub enum EncumbranceStatus {
545 Active = 0,
547 Pending = 1,
549 Subordinated = 2,
551 Released = 3,
553 Foreclosed = 4,
555 Expired = 5,
557 Disputed = 6,
559 Voided = 7,
561}
562
563#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
565#[repr(u8)]
566pub enum PriorityPosition {
567 First = 1,
568 Second = 2,
569 Third = 3,
570 Fourth = 4,
571 Fifth = 5,
572 Subordinate = 10,
573 Unspecified = 255,
574}
575
576#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
578pub struct Encumbrance {
579 pub encumbrance_id: EncumbranceId,
581 pub asset_id: AssetId,
583 pub encumbrance_type: EncumbranceType,
585 pub encumbrance_commitment: [u8; 32],
587 pub holder_ref: PartyRef,
589 pub obligor_ref: Option<PartyRef>,
591 pub priority: PriorityPosition,
593 pub amount_commitment: Option<[u8; 32]>,
595 pub effective_from: Timestamp,
597 pub expiry: Option<Timestamp>,
599 pub issuer_address: Address,
601 pub issuer_class: PropertyIssuerClass,
603 pub policy_id: PolicyId,
605 pub revocation_ref: Option<[u8; 32]>,
607 pub status: EncumbranceStatus,
609 pub created_at: Timestamp,
611 pub updated_at: Timestamp,
613 pub recorded_at_height: BlockHeight,
615 pub agreement_id: Option<[u8; 32]>,
617 pub attachments: Vec<AttachmentRef>,
619}
620
621impl Encumbrance {
622 pub fn generate_id(
624 asset_id: &AssetId,
625 encumbrance_type: EncumbranceType,
626 holder_ref: &PartyRef,
627 nonce: &[u8; 32],
628 ) -> EncumbranceId {
629 let mut hasher = blake3::Hasher::new();
630 hasher.update(ENCUMBRANCE_DOMAIN_SEP);
631 hasher.update(b":v1:");
632 hasher.update(asset_id);
633 hasher.update(&[encumbrance_type as u8]);
634 hasher.update(&holder_ref.as_hash());
635 hasher.update(nonce);
636 *hasher.finalize().as_bytes()
637 }
638
639 pub fn generate_commitment(
641 terms_hash: &[u8; 32],
642 amount_commitment: Option<&[u8; 32]>,
643 parties_commitment: &[u8; 32],
644 ) -> [u8; 32] {
645 let mut hasher = blake3::Hasher::new();
646 hasher.update(ENCUMBRANCE_COMMITMENT_SEP);
647 hasher.update(terms_hash);
648 if let Some(ac) = amount_commitment {
649 hasher.update(ac);
650 }
651 hasher.update(parties_commitment);
652 *hasher.finalize().as_bytes()
653 }
654
655 pub fn is_active(&self, current_time: Timestamp) -> bool {
657 if !matches!(self.status, EncumbranceStatus::Active | EncumbranceStatus::Subordinated) {
658 return false;
659 }
660 if current_time < self.effective_from {
661 return false;
662 }
663 if let Some(exp) = self.expiry {
664 if current_time >= exp {
665 return false;
666 }
667 }
668 true
669 }
670}
671
672#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
678#[repr(u8)]
679pub enum CoverageType {
680 Homeowners = 0,
683 Renters = 1,
685 Landlord = 2,
687 CommercialProperty = 3,
689 Flood = 4,
691 Earthquake = 5,
693 Fire = 6,
695 Windstorm = 7,
697
698 AutoLiability = 20,
701 AutoCollision = 21,
703 AutoComprehensive = 22,
705 UninsuredMotorist = 23,
707 PersonalInjuryProtection = 24,
709 GapInsurance = 25,
711 Watercraft = 26,
713 Aircraft = 27,
715
716 OwnersTitlePolicy = 40,
719 LendersTitlePolicy = 41,
721 ExtendedCoverage = 42,
723
724 GeneralLiability = 50,
727 ProfessionalLiability = 51,
729 UmbrellaPolicy = 52,
731 ProductLiability = 53,
733
734 BuildersRisk = 60,
737 InlandMarine = 61,
739 ValuableArticles = 62,
741 EquipmentBreakdown = 63,
743
744 Other = 255,
746}
747
748#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
750#[repr(u8)]
751pub enum CoverageStatus {
752 Active = 0,
754 Pending = 1,
756 Suspended = 2,
758 Cancelled = 3,
760 Expired = 4,
762 Lapsed = 5,
764 Renewed = 6,
766 NonRenewed = 7,
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
772pub struct InsuranceCoverage {
773 pub coverage_id: CoverageId,
775 pub asset_id: AssetId,
777 pub coverage_type: CoverageType,
779 pub coverage_commitment: [u8; 32],
781 pub insurer_ref: PartyRef,
783 pub insured_ref: PartyRef,
785 pub additional_insureds: Vec<PartyRef>,
787 pub limit_commitment: [u8; 32],
789 pub deductible_commitment: Option<[u8; 32]>,
791 pub premium_commitment: Option<[u8; 32]>,
793 pub effective_from: Timestamp,
795 pub expiry: Timestamp,
797 pub issuer_address: Address,
799 pub issuer_class: PropertyIssuerClass,
801 pub policy_id: PolicyId,
803 pub revocation_ref: Option<[u8; 32]>,
805 pub status: CoverageStatus,
807 pub created_at: Timestamp,
809 pub updated_at: Timestamp,
811 pub recorded_at_height: BlockHeight,
813 pub prior_coverage_id: Option<CoverageId>,
815 pub attachments: Vec<AttachmentRef>,
817}
818
819impl InsuranceCoverage {
820 pub fn generate_id(
822 asset_id: &AssetId,
823 coverage_type: CoverageType,
824 insurer_ref: &PartyRef,
825 insured_ref: &PartyRef,
826 nonce: &[u8; 32],
827 ) -> CoverageId {
828 let mut hasher = blake3::Hasher::new();
829 hasher.update(COVERAGE_DOMAIN_SEP);
830 hasher.update(b":v1:");
831 hasher.update(asset_id);
832 hasher.update(&[coverage_type as u8]);
833 hasher.update(&insurer_ref.as_hash());
834 hasher.update(&insured_ref.as_hash());
835 hasher.update(nonce);
836 *hasher.finalize().as_bytes()
837 }
838
839 pub fn generate_commitment(
841 policy_terms_hash: &[u8; 32],
842 limit_commitment: &[u8; 32],
843 exclusions_hash: Option<&[u8; 32]>,
844 ) -> [u8; 32] {
845 let mut hasher = blake3::Hasher::new();
846 hasher.update(COVERAGE_COMMITMENT_SEP);
847 hasher.update(policy_terms_hash);
848 hasher.update(limit_commitment);
849 if let Some(eh) = exclusions_hash {
850 hasher.update(eh);
851 }
852 *hasher.finalize().as_bytes()
853 }
854
855 pub fn is_in_force(&self, current_time: Timestamp) -> bool {
857 if !matches!(self.status, CoverageStatus::Active | CoverageStatus::Renewed) {
858 return false;
859 }
860 if current_time < self.effective_from {
861 return false;
862 }
863 if current_time >= self.expiry {
864 return false;
865 }
866 true
867 }
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
876#[repr(u8)]
877pub enum ClaimType {
878 FireDamage = 0,
881 WaterDamage = 1,
883 StormDamage = 2,
885 Theft = 3,
887 Vandalism = 4,
889 StructuralDamage = 5,
891 FloodDamage = 6,
893 EarthquakeDamage = 7,
895
896 Collision = 20,
899 Comprehensive = 21,
901 HitAndRun = 22,
903 TotalLoss = 23,
905 UninsuredMotoristClaim = 24,
907
908 BodilyInjury = 40,
911 PropertyDamageLiability = 41,
913 PersonalInjuryClaim = 42,
915
916 TitleDefect = 50,
919 LienNotShown = 51,
921 Forgery = 52,
923 Fraud = 53,
925 Encroachment = 54,
927
928 Other = 255,
930}
931
932#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
934#[repr(u8)]
935pub enum ClaimStatus {
936 Filed = 0,
938 Acknowledged = 1,
940 UnderInvestigation = 2,
942 PendingDocumentation = 3,
944 InReview = 4,
946 Approved = 5,
948 PartiallyApproved = 6,
950 Denied = 7,
952 Paid = 8,
954 Closed = 9,
956 Reopened = 10,
958 InLitigation = 11,
960 SubrogationPending = 12,
962 Withdrawn = 13,
964}
965
966#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
968pub struct InsuranceClaim {
969 pub claim_id: ClaimId,
971 pub coverage_id: CoverageId,
973 pub asset_id: AssetId,
975 pub claim_type: ClaimType,
977 pub claim_commitment: [u8; 32],
979 pub claimant_ref: PartyRef,
981 pub date_of_loss: Timestamp,
983 pub date_filed: Timestamp,
985 pub loss_amount_commitment: Option<[u8; 32]>,
987 pub approved_amount_commitment: Option<[u8; 32]>,
989 pub paid_amount_commitment: Option<[u8; 32]>,
991 pub adjuster_ref: Option<PartyRef>,
993 pub issuer_address: Address,
995 pub issuer_class: PropertyIssuerClass,
997 pub policy_id: PolicyId,
999 pub revocation_ref: Option<[u8; 32]>,
1001 pub status: ClaimStatus,
1003 pub created_at: Timestamp,
1005 pub updated_at: Timestamp,
1007 pub recorded_at_height: BlockHeight,
1009 pub related_claims: Vec<ClaimId>,
1011 pub attachments: Vec<AttachmentRef>,
1013}
1014
1015impl InsuranceClaim {
1016 pub fn generate_id(
1018 coverage_id: &CoverageId,
1019 claim_type: ClaimType,
1020 claimant_ref: &PartyRef,
1021 date_of_loss: Timestamp,
1022 nonce: &[u8; 32],
1023 ) -> ClaimId {
1024 let mut hasher = blake3::Hasher::new();
1025 hasher.update(CLAIM_DOMAIN_SEP);
1026 hasher.update(b":v1:");
1027 hasher.update(coverage_id);
1028 hasher.update(&[claim_type as u8]);
1029 hasher.update(&claimant_ref.as_hash());
1030 hasher.update(&date_of_loss.to_le_bytes());
1031 hasher.update(nonce);
1032 *hasher.finalize().as_bytes()
1033 }
1034
1035 pub fn generate_commitment(
1037 loss_description_hash: &[u8; 32],
1038 loss_amount_commitment: Option<&[u8; 32]>,
1039 documentation_hash: Option<&[u8; 32]>,
1040 ) -> [u8; 32] {
1041 let mut hasher = blake3::Hasher::new();
1042 hasher.update(CLAIM_COMMITMENT_SEP);
1043 hasher.update(loss_description_hash);
1044 if let Some(lac) = loss_amount_commitment {
1045 hasher.update(lac);
1046 }
1047 if let Some(dh) = documentation_hash {
1048 hasher.update(dh);
1049 }
1050 *hasher.finalize().as_bytes()
1051 }
1052
1053 pub fn is_open(&self) -> bool {
1055 matches!(
1056 self.status,
1057 ClaimStatus::Filed
1058 | ClaimStatus::Acknowledged
1059 | ClaimStatus::UnderInvestigation
1060 | ClaimStatus::PendingDocumentation
1061 | ClaimStatus::InReview
1062 | ClaimStatus::Reopened
1063 | ClaimStatus::InLitigation
1064 | ClaimStatus::SubrogationPending
1065 )
1066 }
1067
1068 pub fn is_resolved(&self) -> bool {
1070 matches!(
1071 self.status,
1072 ClaimStatus::Approved
1073 | ClaimStatus::PartiallyApproved
1074 | ClaimStatus::Denied
1075 | ClaimStatus::Paid
1076 | ClaimStatus::Closed
1077 | ClaimStatus::Withdrawn
1078 )
1079 }
1080}
1081
1082#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1088#[repr(u8)]
1089pub enum PropertyProofProfile {
1090 OwnershipProof = 0,
1092 EncumbranceStatus = 1,
1094 CoverageExists = 2,
1096 CoverageInForce = 3,
1098 ClaimStatus = 4,
1100 TitleEventOccurred = 5,
1102 AssetTypeProof = 6,
1104 NoLiensOfType = 7,
1106}
1107
1108#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1110#[repr(u8)]
1111pub enum PropertyProofType {
1112 Mock = 0,
1114 Groth16 = 1,
1116 Plonk = 2,
1118 ThresholdSignature = 3,
1120 MerkleInclusion = 4,
1122}
1123
1124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1126pub struct PropertyProofEnvelope {
1127 pub proof_id: ProofId,
1129 pub profile: PropertyProofProfile,
1131 pub profile_id: String,
1133 pub policy_ids: Vec<PolicyId>,
1135 pub public_inputs: Vec<u8>,
1137 pub proof_data: Vec<u8>,
1139 pub proof_type: PropertyProofType,
1141 pub subject_nullifier: [u8; 32],
1143 pub generated_at: Timestamp,
1145 pub expires_at: Timestamp,
1147}
1148
1149impl PropertyProofEnvelope {
1150 pub fn generate_id(
1152 profile: PropertyProofProfile,
1153 subject_nullifier: &[u8; 32],
1154 policy_ids: &[PolicyId],
1155 nonce: &[u8; 32],
1156 ) -> ProofId {
1157 let mut hasher = blake3::Hasher::new();
1158 hasher.update(PROPERTY_PROOF_DOMAIN_SEP);
1159 hasher.update(b":v1:");
1160 hasher.update(&[profile as u8]);
1161 hasher.update(subject_nullifier);
1162 for policy_id in policy_ids {
1163 hasher.update(policy_id);
1164 }
1165 hasher.update(nonce);
1166 *hasher.finalize().as_bytes()
1167 }
1168}
1169
1170#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1176#[repr(u8)]
1177pub enum PropertyOperation {
1178 AnchorAsset = 0,
1180 UpdateAsset = 1,
1181 TransferAsset = 2,
1182 MergeAssets = 3,
1183 SubdivideAsset = 4,
1184 DeregisterAsset = 5,
1185
1186 RecordTitleEvent = 10,
1188 UpdateTitleEvent = 11,
1189 SupersedeTitleEvent = 12,
1190 VoidTitleEvent = 13,
1191
1192 RecordEncumbrance = 20,
1194 UpdateEncumbrance = 21,
1195 SubordinateEncumbrance = 22,
1196 ReleaseEncumbrance = 23,
1197 ForecloseEncumbrance = 24,
1198
1199 IssueCoverage = 30,
1201 UpdateCoverage = 31,
1202 RenewCoverage = 32,
1203 CancelCoverage = 33,
1204 SuspendCoverage = 34,
1205 ReinstateCoverage = 35,
1206
1207 FileClaim = 40,
1209 UpdateClaim = 41,
1210 ApproveClaim = 42,
1211 DenyClaim = 43,
1212 PayClaim = 44,
1213 CloseClaim = 45,
1214 ReopenClaim = 46,
1215 WithdrawClaim = 47,
1216
1217 SubmitProof = 50,
1219 VerifyProof = 51,
1220}
1221
1222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1224pub struct PropertyTxData {
1225 pub operation: PropertyOperation,
1226 pub data: Vec<u8>,
1227 pub recipient: crate::Address,
1229}
1230
1231#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1237pub enum PropertyEvent {
1238 AssetAnchored {
1240 asset_id: AssetId,
1241 asset_type: AssetType,
1242 jurisdiction: String,
1243 timestamp: Timestamp,
1244 },
1245 AssetUpdated {
1246 asset_id: AssetId,
1247 new_status: AssetStatus,
1248 timestamp: Timestamp,
1249 },
1250 AssetTransferred {
1251 asset_id: AssetId,
1252 from_ref_hash: [u8; 32],
1253 to_ref_hash: [u8; 32],
1254 timestamp: Timestamp,
1255 },
1256
1257 TitleEventRecorded {
1259 event_id: TitleEventId,
1260 asset_id: AssetId,
1261 event_type: TitleEventType,
1262 timestamp: Timestamp,
1263 },
1264 TitleEventSuperseded {
1265 old_event_id: TitleEventId,
1266 new_event_id: TitleEventId,
1267 timestamp: Timestamp,
1268 },
1269
1270 EncumbranceRecorded {
1272 encumbrance_id: EncumbranceId,
1273 asset_id: AssetId,
1274 encumbrance_type: EncumbranceType,
1275 priority: PriorityPosition,
1276 timestamp: Timestamp,
1277 },
1278 EncumbranceReleased {
1279 encumbrance_id: EncumbranceId,
1280 asset_id: AssetId,
1281 timestamp: Timestamp,
1282 },
1283 EncumbranceForeclosed {
1284 encumbrance_id: EncumbranceId,
1285 asset_id: AssetId,
1286 timestamp: Timestamp,
1287 },
1288
1289 CoverageIssued {
1291 coverage_id: CoverageId,
1292 asset_id: AssetId,
1293 coverage_type: CoverageType,
1294 effective_from: Timestamp,
1295 expiry: Timestamp,
1296 timestamp: Timestamp,
1297 },
1298 CoverageStatusUpdated {
1299 coverage_id: CoverageId,
1300 new_status: CoverageStatus,
1301 timestamp: Timestamp,
1302 },
1303
1304 ClaimFiled {
1306 claim_id: ClaimId,
1307 coverage_id: CoverageId,
1308 claim_type: ClaimType,
1309 date_of_loss: Timestamp,
1310 timestamp: Timestamp,
1311 },
1312 ClaimStatusUpdated {
1313 claim_id: ClaimId,
1314 new_status: ClaimStatus,
1315 timestamp: Timestamp,
1316 },
1317 ClaimPaid {
1318 claim_id: ClaimId,
1319 amount_commitment: [u8; 32],
1320 timestamp: Timestamp,
1321 },
1322
1323 PropertyProofSubmitted {
1325 proof_id: ProofId,
1326 profile: PropertyProofProfile,
1327 timestamp: Timestamp,
1328 },
1329 PropertyProofVerified {
1330 proof_id: ProofId,
1331 valid: bool,
1332 timestamp: Timestamp,
1333 },
1334}
1335
1336#[cfg(test)]
1337mod tests {
1338 use super::*;
1339
1340 #[test]
1341 fn test_asset_id_generation() {
1342 let issuer = Address::new([1u8; 20]);
1343 let commitment = [2u8; 32];
1344 let nonce = [3u8; 32];
1345
1346 let id = AssetAnchor::generate_id(
1347 &issuer,
1348 &commitment,
1349 AssetType::SingleFamilyResidence,
1350 "US-CA-LA",
1351 &nonce,
1352 );
1353 assert_ne!(id, [0u8; 32]);
1354
1355 let id2 = AssetAnchor::generate_id(
1357 &issuer,
1358 &commitment,
1359 AssetType::SingleFamilyResidence,
1360 "US-CA-LA",
1361 &nonce,
1362 );
1363 assert_eq!(id, id2);
1364
1365 let id3 = AssetAnchor::generate_id(
1367 &issuer,
1368 &commitment,
1369 AssetType::Commercial,
1370 "US-CA-LA",
1371 &nonce,
1372 );
1373 assert_ne!(id, id3);
1374 }
1375
1376 #[test]
1377 fn test_asset_commitment() {
1378 let details = b"property details here";
1379 let commitment = AssetAnchor::generate_commitment(details, None, None);
1380 assert_ne!(commitment, [0u8; 32]);
1381 }
1382
1383 #[test]
1384 fn test_title_event_id() {
1385 let asset_id = [1u8; 32];
1386 let issuer = Address::new([2u8; 20]);
1387 let nonce = [3u8; 32];
1388
1389 let id = TitleEvent::generate_id(
1390 &asset_id,
1391 TitleEventType::DeedRecorded,
1392 &issuer,
1393 &nonce,
1394 );
1395 assert_ne!(id, [0u8; 32]);
1396 }
1397
1398 #[test]
1399 fn test_encumbrance_active() {
1400 let encumbrance = Encumbrance {
1401 encumbrance_id: [1u8; 32],
1402 asset_id: [2u8; 32],
1403 encumbrance_type: EncumbranceType::FirstMortgage,
1404 encumbrance_commitment: [3u8; 32],
1405 holder_ref: PartyRef::Commitment([4u8; 32]),
1406 obligor_ref: Some(PartyRef::Commitment([5u8; 32])),
1407 priority: PriorityPosition::First,
1408 amount_commitment: Some([6u8; 32]),
1409 effective_from: 1000,
1410 expiry: Some(2000),
1411 issuer_address: Address::new([7u8; 20]),
1412 issuer_class: PropertyIssuerClass::MortgageLender,
1413 policy_id: [8u8; 32],
1414 revocation_ref: None,
1415 status: EncumbranceStatus::Active,
1416 created_at: 1000,
1417 updated_at: 1000,
1418 recorded_at_height: 100,
1419 agreement_id: None,
1420 attachments: vec![],
1421 };
1422
1423 assert!(!encumbrance.is_active(500)); assert!(encumbrance.is_active(1500)); assert!(!encumbrance.is_active(2500)); }
1427
1428 #[test]
1429 fn test_coverage_in_force() {
1430 let coverage = InsuranceCoverage {
1431 coverage_id: [1u8; 32],
1432 asset_id: [2u8; 32],
1433 coverage_type: CoverageType::Homeowners,
1434 coverage_commitment: [3u8; 32],
1435 insurer_ref: PartyRef::Commitment([4u8; 32]),
1436 insured_ref: PartyRef::Commitment([5u8; 32]),
1437 additional_insureds: vec![],
1438 limit_commitment: [6u8; 32],
1439 deductible_commitment: None,
1440 premium_commitment: None,
1441 effective_from: 1000,
1442 expiry: 2000,
1443 issuer_address: Address::new([7u8; 20]),
1444 issuer_class: PropertyIssuerClass::InsuranceCompany,
1445 policy_id: [8u8; 32],
1446 revocation_ref: None,
1447 status: CoverageStatus::Active,
1448 created_at: 1000,
1449 updated_at: 1000,
1450 recorded_at_height: 100,
1451 prior_coverage_id: None,
1452 attachments: vec![],
1453 };
1454
1455 assert!(!coverage.is_in_force(500)); assert!(coverage.is_in_force(1500)); assert!(!coverage.is_in_force(2500)); }
1459
1460 #[test]
1461 fn test_claim_status() {
1462 let claim = InsuranceClaim {
1463 claim_id: [1u8; 32],
1464 coverage_id: [2u8; 32],
1465 asset_id: [3u8; 32],
1466 claim_type: ClaimType::WaterDamage,
1467 claim_commitment: [4u8; 32],
1468 claimant_ref: PartyRef::Commitment([5u8; 32]),
1469 date_of_loss: 900,
1470 date_filed: 1000,
1471 loss_amount_commitment: Some([6u8; 32]),
1472 approved_amount_commitment: None,
1473 paid_amount_commitment: None,
1474 adjuster_ref: None,
1475 issuer_address: Address::new([7u8; 20]),
1476 issuer_class: PropertyIssuerClass::InsuranceCompany,
1477 policy_id: [8u8; 32],
1478 revocation_ref: None,
1479 status: ClaimStatus::Filed,
1480 created_at: 1000,
1481 updated_at: 1000,
1482 recorded_at_height: 100,
1483 related_claims: vec![],
1484 attachments: vec![],
1485 };
1486
1487 assert!(claim.is_open());
1488 assert!(!claim.is_resolved());
1489 }
1490
1491 #[test]
1492 fn test_property_issuer_class() {
1493 assert!(PropertyIssuerClass::LandRegistry.is_official());
1494 assert!(PropertyIssuerClass::MotorVehicleDept.is_official());
1495 assert!(!PropertyIssuerClass::TitleCompany.is_official());
1496
1497 assert!(PropertyIssuerClass::TitleCompany.is_lowkey());
1498 assert!(PropertyIssuerClass::MortgageLender.is_lowkey());
1499 assert!(!PropertyIssuerClass::LandRegistry.is_lowkey());
1500 }
1501
1502 #[test]
1503 fn test_property_proof_id() {
1504 let nullifier = [1u8; 32];
1505 let policies = vec![[2u8; 32], [3u8; 32]];
1506 let nonce = [4u8; 32];
1507
1508 let id = PropertyProofEnvelope::generate_id(
1509 PropertyProofProfile::OwnershipProof,
1510 &nullifier,
1511 &policies,
1512 &nonce,
1513 );
1514 assert_ne!(id, [0u8; 32]);
1515 }
1516}