Skip to main content

sumchain_primitives/
agreement.rs

1//! SRC-84X Legal Instruments, IP, Notary & Attestation Domain
2//!
3//! Privacy-first infrastructure for:
4//! - SRC-841: Agreement Commitments
5//! - SRC-842: Party Signatures & Role Binding
6//! - SRC-843: Notary & Attestation Packets
7//! - SRC-844: IP Rights & Creative Actions
8//! - SRC-845: Execution Link Standard
9//! - SRC-846: 84X Proof Profiles
10
11use serde::{Deserialize, Serialize};
12use crate::{Address, BlockHeight, Timestamp};
13
14// =============================================================================
15// Type Aliases
16// =============================================================================
17
18/// Unique agreement identifier
19pub type AgreementId = [u8; 32];
20/// Unique signature identifier
21pub type SignatureId = [u8; 32];
22/// Notary attestation identifier
23pub type AttestationId = [u8; 32];
24/// IP asset identifier
25pub type IpAssetId = [u8; 32];
26/// Executor link identifier
27pub type ExecutorLinkId = [u8; 32];
28/// Policy identifier (SRC-803 compatible)
29pub type PolicyId = [u8; 32];
30/// Proof identifier (SRC-806 compatible)
31pub type ProofId = [u8; 32];
32/// Subject identifier (SRC-801 compatible)
33pub type SubjectId = [u8; 32];
34
35// =============================================================================
36// Domain Separators (for deterministic hashing)
37// =============================================================================
38
39pub const AGREEMENT_DOMAIN_SEP: &[u8] = b"SRC841-AGREEMENT:";
40pub const AGREEMENT_COMMITMENT_SEP: &[u8] = b"SRC841-COMMITMENT:v1:";
41pub const PARTY_COMMITMENT_SEP: &[u8] = b"SRC841-PARTY:v1:";
42pub const SIGNATURE_DOMAIN_SEP: &[u8] = b"SRC842-SIGNATURE:";
43pub const SIGNATURE_MESSAGE_SEP: &[u8] = b"SRC842-SIGN-MSG:v1:";
44pub const ATTESTATION_DOMAIN_SEP: &[u8] = b"SRC843-ATTESTATION:";
45pub const NOTARY_COMMITMENT_SEP: &[u8] = b"SRC843-NOTARY:v1:";
46pub const IP_ACTION_DOMAIN_SEP: &[u8] = b"SRC844-IP-ACTION:";
47pub const IP_SCOPE_COMMITMENT_SEP: &[u8] = b"SRC844-SCOPE:v1:";
48pub const EXECUTOR_LINK_DOMAIN_SEP: &[u8] = b"SRC845-EXECUTOR:";
49pub const TERMS_COMMITMENT_SEP: &[u8] = b"SRC845-TERMS:v1:";
50pub const PROOF_PROFILE_DOMAIN_SEP: &[u8] = b"SRC846-PROOF:";
51
52// =============================================================================
53// SRC-841: Agreement Commitment
54// =============================================================================
55
56/// Party reference - supports both privacy-preserving commitments and explicit subjects
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub enum PartyRef {
59    /// Privacy-preserving: BLAKE3 commitment of party identity
60    Commitment([u8; 32]),
61    /// Explicit subject ID (SRC-801) - only when party consents
62    Subject(SubjectId),
63}
64
65impl PartyRef {
66    /// Generate a party commitment from subject and salt
67    pub fn generate_commitment(subject: &SubjectId, salt: &[u8; 32]) -> [u8; 32] {
68        let mut hasher = blake3::Hasher::new();
69        hasher.update(PARTY_COMMITMENT_SEP);
70        hasher.update(subject);
71        hasher.update(salt);
72        *hasher.finalize().as_bytes()
73    }
74
75    /// Get the hash for indexing (works for both variants)
76    pub fn as_hash(&self) -> [u8; 32] {
77        match self {
78            PartyRef::Commitment(c) => *c,
79            PartyRef::Subject(s) => *s,
80        }
81    }
82}
83
84/// Role of a party in an agreement
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[repr(u8)]
87pub enum AgreementRole {
88    Buyer = 0,
89    Seller = 1,
90    Employer = 2,
91    Employee = 3,
92    Landlord = 4,
93    Tenant = 5,
94    Licensor = 6,
95    Licensee = 7,
96    Lender = 8,
97    Borrower = 9,
98    Guarantor = 10,
99    Beneficiary = 11,
100    Trustee = 12,
101    Settlor = 13,
102    Assignor = 14,
103    Assignee = 15,
104    Principal = 16,
105    Agent = 17,
106    Partner = 18,
107    Shareholder = 19,
108    Witness = 20,
109    Notary = 21,
110    Mediator = 22,
111    Arbitrator = 23,
112    Other = 255,
113}
114
115/// Encrypted attachment reference (no plaintext on-chain)
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct AttachmentRef {
118    /// BLAKE3 hash of the encrypted payload
119    pub payload_hash: [u8; 32],
120    /// Size of the payload in bytes
121    pub payload_size: u64,
122    /// Optional URI hint for retrieval (e.g., "ipfs://...", "https://...")
123    pub hint_uri: Option<String>,
124    /// Encryption metadata (algorithm, key commitment, etc.)
125    pub encryption_meta: Option<EncryptionMeta>,
126}
127
128/// Encryption metadata for attachments
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
130pub struct EncryptionMeta {
131    /// Encryption algorithm used
132    pub algorithm: EncryptionAlgorithm,
133    /// Commitment to the encryption key (for key escrow/recovery)
134    pub key_commitment: Option<[u8; 32]>,
135    /// Nonce/IV if applicable
136    pub nonce: Option<Vec<u8>>,
137}
138
139/// Supported encryption algorithms
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
141#[repr(u8)]
142pub enum EncryptionAlgorithm {
143    /// AES-256-GCM
144    Aes256Gcm = 0,
145    /// ChaCha20-Poly1305
146    ChaCha20Poly1305 = 1,
147    /// X25519 + AES-256-GCM (hybrid)
148    X25519Aes256Gcm = 2,
149    /// Threshold encryption scheme
150    ThresholdEncryption = 3,
151}
152
153/// Party binding within an agreement
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
155pub struct PartyBinding {
156    /// Party reference (commitment or subject)
157    pub party_ref: PartyRef,
158    /// Role in the agreement
159    pub role: AgreementRole,
160    /// Whether this party has signed
161    pub signed: bool,
162    /// Signature timestamp if signed
163    pub signed_at: Option<Timestamp>,
164}
165
166/// Agreement status
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
168#[repr(u8)]
169pub enum AgreementStatus {
170    /// Draft - not yet finalized
171    Draft = 0,
172    /// Pending signatures
173    PendingSignatures = 1,
174    /// Fully executed (all required signatures)
175    Executed = 2,
176    /// Active and in effect
177    Active = 3,
178    /// Expired by time
179    Expired = 4,
180    /// Terminated by parties
181    Terminated = 5,
182    /// Superseded by another agreement
183    Superseded = 6,
184    /// Voided (never became effective)
185    Voided = 7,
186}
187
188/// SRC-841 Agreement Commitment
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
190pub struct AgreementCommitment {
191    /// Unique agreement identifier
192    pub agreement_id: AgreementId,
193    /// BLAKE3 hash of canonical agreement content
194    pub agreement_commitment: [u8; 32],
195    /// Parties and their roles
196    pub parties: Vec<PartyBinding>,
197    /// Jurisdiction code (e.g., "US-DE", "UK", "SG")
198    pub jurisdiction_code: String,
199    /// When the agreement becomes effective
200    pub effective_from: Option<Timestamp>,
201    /// When the agreement expires
202    pub expiry: Option<Timestamp>,
203    /// Encrypted attachment references
204    pub attachments: Vec<AttachmentRef>,
205    /// Policy ID governing signature requirements
206    pub policy_id: PolicyId,
207    /// Current status
208    pub status: AgreementStatus,
209    /// Creation timestamp
210    pub created_at: Timestamp,
211    /// Last update timestamp
212    pub updated_at: Timestamp,
213    /// Block height when created
214    pub created_at_height: BlockHeight,
215    /// Optional: supersedes another agreement
216    pub supersedes: Option<AgreementId>,
217}
218
219impl AgreementCommitment {
220    /// Generate deterministic agreement ID
221    pub fn generate_id(
222        creator: &Address,
223        agreement_commitment: &[u8; 32],
224        nonce: &[u8; 32],
225    ) -> AgreementId {
226        let mut hasher = blake3::Hasher::new();
227        hasher.update(AGREEMENT_DOMAIN_SEP);
228        hasher.update(b":v1:");
229        hasher.update(creator.as_ref());
230        hasher.update(agreement_commitment);
231        hasher.update(nonce);
232        *hasher.finalize().as_bytes()
233    }
234
235    /// Generate commitment from agreement terms
236    pub fn generate_commitment(
237        terms_data: &[u8],
238        schema_hash: &[u8; 32],
239        version: u32,
240    ) -> [u8; 32] {
241        let mut hasher = blake3::Hasher::new();
242        hasher.update(AGREEMENT_COMMITMENT_SEP);
243        hasher.update(schema_hash);
244        hasher.update(&version.to_le_bytes());
245        hasher.update(terms_data);
246        *hasher.finalize().as_bytes()
247    }
248
249    /// Check if all required parties have signed
250    pub fn is_fully_signed(&self) -> bool {
251        self.parties.iter().all(|p| p.signed)
252    }
253
254    /// Count signed parties
255    pub fn signed_count(&self) -> usize {
256        self.parties.iter().filter(|p| p.signed).count()
257    }
258}
259
260// =============================================================================
261// SRC-842: Party Signatures & Role Binding
262// =============================================================================
263
264/// Signature type for agreements
265#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
266pub enum SignatureType {
267    /// Single ECDSA/Ed25519 signature
268    Single,
269    /// Threshold signature (t-of-n)
270    Threshold { threshold: u8, total: u8 },
271    /// Multi-signature (all required)
272    Multi { required: u8 },
273}
274
275/// Party signature record
276#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
277pub struct PartySignature {
278    /// Signature identifier
279    pub signature_id: SignatureId,
280    /// Agreement being signed
281    pub agreement_id: AgreementId,
282    /// Party reference (commitment or subject)
283    pub party_ref: PartyRef,
284    /// Role being signed for
285    pub role: AgreementRole,
286    /// Signature type
287    pub signature_type: SignatureType,
288    /// The actual signature bytes
289    pub signature: Vec<u8>,
290    /// Signer's public key or commitment
291    pub signer_key: [u8; 32],
292    /// Signing timestamp
293    pub signed_at: Timestamp,
294    /// Block height when recorded
295    pub recorded_at_height: BlockHeight,
296    /// Optional: witness or notary attestation
297    pub witness_attestation_id: Option<AttestationId>,
298}
299
300impl PartySignature {
301    /// Generate signature ID
302    pub fn generate_id(
303        agreement_id: &AgreementId,
304        party_ref: &PartyRef,
305        role: AgreementRole,
306        nonce: &[u8; 32],
307    ) -> SignatureId {
308        let mut hasher = blake3::Hasher::new();
309        hasher.update(SIGNATURE_DOMAIN_SEP);
310        hasher.update(b":v1:");
311        hasher.update(agreement_id);
312        hasher.update(&party_ref.as_hash());
313        hasher.update(&[role as u8]);
314        hasher.update(nonce);
315        *hasher.finalize().as_bytes()
316    }
317
318    /// Generate the message to be signed (domain-separated)
319    pub fn generate_signing_message(
320        agreement_id: &AgreementId,
321        agreement_commitment: &[u8; 32],
322        party_ref: &PartyRef,
323        role: AgreementRole,
324        policy_id: &PolicyId,
325    ) -> [u8; 32] {
326        let mut hasher = blake3::Hasher::new();
327        hasher.update(SIGNATURE_MESSAGE_SEP);
328        hasher.update(agreement_id);
329        hasher.update(agreement_commitment);
330        hasher.update(&party_ref.as_hash());
331        hasher.update(&[role as u8]);
332        hasher.update(policy_id);
333        *hasher.finalize().as_bytes()
334    }
335}
336
337// =============================================================================
338// SRC-843: Notary & Attestation Packet
339// =============================================================================
340
341/// Notary/attestation issuer class (SRC-802 compatible)
342#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
343#[repr(u8)]
344pub enum AttestationIssuerClass {
345    /// Certified notary public
346    NotaryPublic = 0,
347    /// Law firm / attorney
348    LawFirm = 1,
349    /// Licensed auditor
350    Auditor = 2,
351    /// Certified public accountant
352    Cpa = 3,
353    /// Court official
354    CourtOfficial = 4,
355    /// Government agency
356    GovernmentAgency = 5,
357    /// Registered agent
358    RegisteredAgent = 6,
359    /// Escrow agent
360    EscrowAgent = 7,
361    /// Title company
362    TitleCompany = 8,
363    /// Other authorized
364    Other = 255,
365}
366
367/// Attestation type
368#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
369#[repr(u8)]
370pub enum AttestationType {
371    /// Document notarization
372    Notarization = 0,
373    /// Signature witnessing
374    SignatureWitness = 1,
375    /// Identity verification
376    IdentityVerification = 2,
377    /// Document authentication
378    DocumentAuthentication = 3,
379    /// Apostille
380    Apostille = 4,
381    /// Certification
382    Certification = 5,
383    /// Acknowledgment
384    Acknowledgment = 6,
385    /// Jurat (oath/affirmation)
386    Jurat = 7,
387    /// Copy certification
388    CopyCertification = 8,
389}
390
391/// Attestation status
392#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
393#[repr(u8)]
394pub enum AttestationStatus {
395    Active = 0,
396    Expired = 1,
397    Revoked = 2,
398    Superseded = 3,
399}
400
401/// SRC-843 Notary & Attestation Packet
402#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
403pub struct AttestationPacket {
404    /// Unique attestation identifier
405    pub attestation_id: AttestationId,
406    /// Target: agreement ID or document hash
407    pub target_ref: AttestationTarget,
408    /// Notary/issuer address (must be SRC-802 registered)
409    pub issuer_address: Address,
410    /// Issuer class
411    pub issuer_class: AttestationIssuerClass,
412    /// Type of attestation
413    pub attestation_type: AttestationType,
414    /// Notary commitment (commission/venue/procedure)
415    pub notary_commitment: [u8; 32],
416    /// Jurisdiction code
417    pub jurisdiction_code: String,
418    /// When attestation becomes valid
419    pub valid_from: Timestamp,
420    /// When attestation expires
421    pub expiry: Option<Timestamp>,
422    /// Revocation reference (SRC-805 compatible)
423    pub revocation_ref: Option<[u8; 32]>,
424    /// Status
425    pub status: AttestationStatus,
426    /// Creation timestamp
427    pub created_at: Timestamp,
428    /// Block height when recorded
429    pub recorded_at_height: BlockHeight,
430    /// Policy ID used for authorization
431    pub policy_id: PolicyId,
432}
433
434/// Target of an attestation
435#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
436pub enum AttestationTarget {
437    /// Attest to an agreement
438    Agreement(AgreementId),
439    /// Attest to a document hash
440    DocumentHash([u8; 32]),
441    /// Attest to a signature
442    Signature(SignatureId),
443    /// Attest to an IP action
444    IpAction(IpAssetId),
445}
446
447impl AttestationPacket {
448    /// Generate attestation ID
449    pub fn generate_id(
450        issuer: &Address,
451        target: &AttestationTarget,
452        attestation_type: AttestationType,
453        nonce: &[u8; 32],
454    ) -> AttestationId {
455        let mut hasher = blake3::Hasher::new();
456        hasher.update(ATTESTATION_DOMAIN_SEP);
457        hasher.update(b":v1:");
458        hasher.update(issuer.as_ref());
459        match target {
460            AttestationTarget::Agreement(id) => {
461                hasher.update(b"agreement:");
462                hasher.update(id);
463            }
464            AttestationTarget::DocumentHash(h) => {
465                hasher.update(b"document:");
466                hasher.update(h);
467            }
468            AttestationTarget::Signature(id) => {
469                hasher.update(b"signature:");
470                hasher.update(id);
471            }
472            AttestationTarget::IpAction(id) => {
473                hasher.update(b"ip_action:");
474                hasher.update(id);
475            }
476        }
477        hasher.update(&[attestation_type as u8]);
478        hasher.update(nonce);
479        *hasher.finalize().as_bytes()
480    }
481
482    /// Generate notary commitment
483    pub fn generate_notary_commitment(
484        commission_number: &str,
485        venue: &str,
486        procedure_hash: &[u8; 32],
487    ) -> [u8; 32] {
488        let mut hasher = blake3::Hasher::new();
489        hasher.update(NOTARY_COMMITMENT_SEP);
490        hasher.update(commission_number.as_bytes());
491        hasher.update(venue.as_bytes());
492        hasher.update(procedure_hash);
493        *hasher.finalize().as_bytes()
494    }
495}
496
497// =============================================================================
498// SRC-844: IP Rights & Creative Actions
499// =============================================================================
500
501/// IP action type
502#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
503#[repr(u8)]
504pub enum IpActionType {
505    /// Full assignment of rights
506    Assignment = 0,
507    /// License grant
508    License = 1,
509    /// Pledge/security interest
510    Pledge = 2,
511    /// Release to public domain
512    Release = 3,
513    /// Exclusive license
514    ExclusiveLicense = 4,
515    /// Non-exclusive license
516    NonExclusiveLicense = 5,
517    /// Sublicense
518    Sublicense = 6,
519    /// Termination of license
520    LicenseTermination = 7,
521    /// Transfer of pledge
522    PledgeTransfer = 8,
523    /// Release of pledge
524    PledgeRelease = 9,
525}
526
527/// IP asset type
528#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
529#[repr(u8)]
530pub enum IpAssetType {
531    Patent = 0,
532    Trademark = 1,
533    Copyright = 2,
534    TradeSecret = 3,
535    Design = 4,
536    Software = 5,
537    Database = 6,
538    Domain = 7,
539    Other = 255,
540}
541
542/// IP action status
543#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
544#[repr(u8)]
545pub enum IpActionStatus {
546    Pending = 0,
547    Active = 1,
548    Expired = 2,
549    Revoked = 3,
550    Superseded = 4,
551    Terminated = 5,
552}
553
554/// SRC-844 IP Rights Action
555#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
556pub struct IpRightsAction {
557    /// Unique action identifier
558    pub action_id: IpAssetId,
559    /// IP asset commitment (hash of asset details)
560    pub ip_asset_commitment: [u8; 32],
561    /// Type of IP asset
562    pub asset_type: IpAssetType,
563    /// Action being taken
564    pub action_type: IpActionType,
565    /// Scope commitment (territory/term/field-of-use)
566    pub scope_commitment: [u8; 32],
567    /// Rights holder party reference
568    pub rightsholder_ref: PartyRef,
569    /// Counterparty reference (assignee, licensee, etc.)
570    pub counterparty_ref: Option<PartyRef>,
571    /// Policy ID governing this action
572    pub policy_id: PolicyId,
573    /// When action becomes effective
574    pub valid_from: Timestamp,
575    /// When action expires
576    pub expiry: Option<Timestamp>,
577    /// Revocation reference (SRC-805 compatible)
578    pub revocation_ref: Option<[u8; 32]>,
579    /// Status
580    pub status: IpActionStatus,
581    /// Creation timestamp
582    pub created_at: Timestamp,
583    /// Block height when recorded
584    pub recorded_at_height: BlockHeight,
585    /// Related agreement ID (if any)
586    pub agreement_id: Option<AgreementId>,
587    /// Attachments (e.g., license terms)
588    pub attachments: Vec<AttachmentRef>,
589}
590
591impl IpRightsAction {
592    /// Generate IP action ID
593    pub fn generate_id(
594        rightsholder: &PartyRef,
595        ip_asset_commitment: &[u8; 32],
596        action_type: IpActionType,
597        nonce: &[u8; 32],
598    ) -> IpAssetId {
599        let mut hasher = blake3::Hasher::new();
600        hasher.update(IP_ACTION_DOMAIN_SEP);
601        hasher.update(b":v1:");
602        hasher.update(&rightsholder.as_hash());
603        hasher.update(ip_asset_commitment);
604        hasher.update(&[action_type as u8]);
605        hasher.update(nonce);
606        *hasher.finalize().as_bytes()
607    }
608
609    /// Generate scope commitment
610    pub fn generate_scope_commitment(
611        territory: &str,
612        field_of_use: &str,
613        duration_secs: Option<u64>,
614        additional_terms: Option<&[u8]>,
615    ) -> [u8; 32] {
616        let mut hasher = blake3::Hasher::new();
617        hasher.update(IP_SCOPE_COMMITMENT_SEP);
618        hasher.update(territory.as_bytes());
619        hasher.update(b"|");
620        hasher.update(field_of_use.as_bytes());
621        if let Some(dur) = duration_secs {
622            hasher.update(b"|");
623            hasher.update(&dur.to_le_bytes());
624        }
625        if let Some(terms) = additional_terms {
626            hasher.update(b"|");
627            hasher.update(terms);
628        }
629        *hasher.finalize().as_bytes()
630    }
631}
632
633// =============================================================================
634// SRC-845: Execution Link Standard
635// =============================================================================
636
637/// Executor lifecycle state
638#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
639#[repr(u8)]
640pub enum ExecutorState {
641    /// Draft - not yet activated
642    Draft = 0,
643    /// Active - executor is operational
644    Active = 1,
645    /// Paused - temporarily suspended
646    Paused = 2,
647    /// Terminated - executor relationship ended
648    Terminated = 3,
649    /// Completed - all obligations fulfilled
650    Completed = 4,
651    /// Disputed - under dispute resolution
652    Disputed = 5,
653}
654
655/// SRC-845 Execution Link
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657pub struct ExecutorLink {
658    /// Unique link identifier
659    pub link_id: ExecutorLinkId,
660    /// Agreement being linked
661    pub agreement_id: AgreementId,
662    /// Executor contract address
663    pub executor_contract: Address,
664    /// Executor interface identifier (function selector or interface hash)
665    pub executor_interface_id: [u8; 32],
666    /// Terms commitment (must match what executor expects)
667    pub terms_commitment: [u8; 32],
668    /// Policy ID for activation/modification
669    pub activation_policy_id: PolicyId,
670    /// Current lifecycle state
671    pub state: ExecutorState,
672    /// When link was created
673    pub created_at: Timestamp,
674    /// Last state change
675    pub updated_at: Timestamp,
676    /// Block height when created
677    pub created_at_height: BlockHeight,
678    /// Optional: proof envelope for activation
679    pub activation_proof_id: Option<ProofId>,
680}
681
682impl ExecutorLink {
683    /// Generate executor link ID
684    pub fn generate_id(
685        agreement_id: &AgreementId,
686        executor_contract: &Address,
687        nonce: &[u8; 32],
688    ) -> ExecutorLinkId {
689        let mut hasher = blake3::Hasher::new();
690        hasher.update(EXECUTOR_LINK_DOMAIN_SEP);
691        hasher.update(b":v1:");
692        hasher.update(agreement_id);
693        hasher.update(executor_contract.as_ref());
694        hasher.update(nonce);
695        *hasher.finalize().as_bytes()
696    }
697
698    /// Generate terms commitment
699    pub fn generate_terms_commitment(
700        terms_data: &[u8],
701        executor_interface_id: &[u8; 32],
702    ) -> [u8; 32] {
703        let mut hasher = blake3::Hasher::new();
704        hasher.update(TERMS_COMMITMENT_SEP);
705        hasher.update(executor_interface_id);
706        hasher.update(terms_data);
707        *hasher.finalize().as_bytes()
708    }
709}
710
711// =============================================================================
712// SRC-846: 84X Proof Profiles
713// =============================================================================
714
715/// Proof profile types for SRC-84X
716#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
717#[repr(u8)]
718pub enum AgreementProofProfile {
719    /// Prove agreement is signed by specified roles
720    SignedByRoles = 0,
721    /// Prove notary attestation exists
722    NotaryAttested = 1,
723    /// Prove IP assignment is valid
724    IpAssignmentValid = 2,
725    /// Prove executor is bound and active
726    ExecutorBoundActive = 3,
727    /// Prove party is bound to agreement
728    PartyBound = 4,
729    /// Prove agreement is in specified status
730    AgreementStatus = 5,
731}
732
733/// Proof type (SRC-806 compatible)
734#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
735#[repr(u8)]
736pub enum AgreementProofType {
737    /// Mock proof (for testing)
738    Mock = 0,
739    /// Groth16 ZK-SNARK
740    Groth16 = 1,
741    /// PLONK proof
742    Plonk = 2,
743    /// Threshold signature proof
744    ThresholdSignature = 3,
745    /// Merkle inclusion proof
746    MerkleInclusion = 4,
747}
748
749/// SRC-846 Agreement Proof Envelope
750#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
751pub struct AgreementProofEnvelope {
752    /// Proof identifier
753    pub proof_id: ProofId,
754    /// Proof profile being proven
755    pub profile: AgreementProofProfile,
756    /// Profile version string (e.g., "agreement.signed_by_roles.v1")
757    pub profile_id: String,
758    /// Policy IDs that were checked
759    pub policy_ids: Vec<PolicyId>,
760    /// Public inputs to the proof
761    pub public_inputs: Vec<u8>,
762    /// The proof data
763    pub proof_data: Vec<u8>,
764    /// Proof type
765    pub proof_type: AgreementProofType,
766    /// Subject nullifier (for revocation checking)
767    pub subject_nullifier: [u8; 32],
768    /// When proof was generated
769    pub generated_at: Timestamp,
770    /// When proof expires
771    pub expires_at: Timestamp,
772}
773
774impl AgreementProofEnvelope {
775    /// Generate proof ID
776    pub fn generate_id(
777        profile: AgreementProofProfile,
778        subject_nullifier: &[u8; 32],
779        policy_ids: &[PolicyId],
780        nonce: &[u8; 32],
781    ) -> ProofId {
782        let mut hasher = blake3::Hasher::new();
783        hasher.update(PROOF_PROFILE_DOMAIN_SEP);
784        hasher.update(b":v1:");
785        hasher.update(&[profile as u8]);
786        hasher.update(subject_nullifier);
787        for policy_id in policy_ids {
788            hasher.update(policy_id);
789        }
790        hasher.update(nonce);
791        *hasher.finalize().as_bytes()
792    }
793}
794
795// =============================================================================
796// Operations
797// =============================================================================
798
799/// SRC-84X Operation codes
800#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
801#[repr(u8)]
802pub enum AgreementOperation {
803    // SRC-841: Agreement Commitment (0-9)
804    CommitAgreement = 0,
805    UpdateAgreement = 1,
806    TerminateAgreement = 2,
807    VoidAgreement = 3,
808    SupersedeAgreement = 4,
809
810    // SRC-842: Party Signatures (10-19)
811    SignAgreement = 10,
812    RevokeSignature = 11,
813    AddParty = 12,
814    RemoveParty = 13,
815
816    // SRC-843: Notary & Attestation (20-29)
817    CreateAttestation = 20,
818    RevokeAttestation = 21,
819    UpdateAttestationStatus = 22,
820
821    // SRC-844: IP Rights (30-39)
822    RecordIpAction = 30,
823    UpdateIpAction = 31,
824    TerminateIpAction = 32,
825    RevokeIpAction = 33,
826
827    // SRC-845: Executor Link (40-49)
828    LinkExecutor = 40,
829    ActivateExecutor = 41,
830    PauseExecutor = 42,
831    ResumeExecutor = 43,
832    TerminateExecutor = 44,
833    CompleteExecutor = 45,
834
835    // SRC-846: Proof Operations (50-59)
836    SubmitProof = 50,
837    VerifyProof = 51,
838}
839
840/// Transaction data for SRC-84X operations
841#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
842pub struct AgreementTxData {
843    pub operation: AgreementOperation,
844    pub data: Vec<u8>,
845    /// Token recipient address - the owner of the minted token
846    pub recipient: crate::Address,
847}
848
849// =============================================================================
850// Events
851// =============================================================================
852
853/// SRC-84X Events
854#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
855pub enum AgreementEvent {
856    // SRC-841 Events
857    AgreementCommitted {
858        agreement_id: AgreementId,
859        policy_id: PolicyId,
860        commitment_hash: [u8; 32],
861        timestamp: Timestamp,
862    },
863    AgreementUpdated {
864        agreement_id: AgreementId,
865        new_status: AgreementStatus,
866        timestamp: Timestamp,
867    },
868    AgreementTerminated {
869        agreement_id: AgreementId,
870        timestamp: Timestamp,
871    },
872    AgreementSuperseded {
873        old_agreement_id: AgreementId,
874        new_agreement_id: AgreementId,
875        timestamp: Timestamp,
876    },
877
878    // SRC-842 Events
879    AgreementSigned {
880        agreement_id: AgreementId,
881        party_ref_hash: [u8; 32],
882        role: AgreementRole,
883        signer: Address,
884        timestamp: Timestamp,
885    },
886    SignatureRevoked {
887        agreement_id: AgreementId,
888        signature_id: SignatureId,
889        timestamp: Timestamp,
890    },
891
892    // SRC-843 Events
893    NotaryAttested {
894        target_id: [u8; 32],
895        notary: Address,
896        attestation_hash: [u8; 32],
897        timestamp: Timestamp,
898    },
899    AttestationRevoked {
900        attestation_id: AttestationId,
901        timestamp: Timestamp,
902    },
903
904    // SRC-844 Events
905    IpActionRecorded {
906        ip_asset_hash: [u8; 32],
907        action_type: IpActionType,
908        policy_id: PolicyId,
909        timestamp: Timestamp,
910    },
911    IpActionUpdated {
912        action_id: IpAssetId,
913        new_status: IpActionStatus,
914        timestamp: Timestamp,
915    },
916
917    // SRC-845 Events
918    ExecutorLinked {
919        agreement_id: AgreementId,
920        executor: Address,
921        interface_id: [u8; 32],
922        state: ExecutorState,
923        timestamp: Timestamp,
924    },
925    ExecutorStateUpdated {
926        agreement_id: AgreementId,
927        link_id: ExecutorLinkId,
928        new_state: ExecutorState,
929        timestamp: Timestamp,
930    },
931
932    // SRC-846 Events
933    ProofSubmitted {
934        proof_id: ProofId,
935        profile: AgreementProofProfile,
936        timestamp: Timestamp,
937    },
938    ProofVerified {
939        proof_id: ProofId,
940        valid: bool,
941        timestamp: Timestamp,
942    },
943}
944
945#[cfg(test)]
946mod tests {
947    use super::*;
948
949    #[test]
950    fn test_party_ref_commitment() {
951        let subject = [1u8; 32];
952        let salt = [2u8; 32];
953        let commitment = PartyRef::generate_commitment(&subject, &salt);
954        assert_ne!(commitment, [0u8; 32]);
955
956        // Different salt = different commitment
957        let salt2 = [3u8; 32];
958        let commitment2 = PartyRef::generate_commitment(&subject, &salt2);
959        assert_ne!(commitment, commitment2);
960    }
961
962    #[test]
963    fn test_agreement_id_generation() {
964        let creator = Address::new([1u8; 20]);
965        let commitment = [2u8; 32];
966        let nonce = [3u8; 32];
967
968        let id = AgreementCommitment::generate_id(&creator, &commitment, &nonce);
969        assert_ne!(id, [0u8; 32]);
970
971        // Same inputs = same ID
972        let id2 = AgreementCommitment::generate_id(&creator, &commitment, &nonce);
973        assert_eq!(id, id2);
974    }
975
976    #[test]
977    fn test_signing_message_domain_separation() {
978        let agreement_id = [1u8; 32];
979        let commitment = [2u8; 32];
980        let party_ref = PartyRef::Commitment([3u8; 32]);
981        let policy_id = [4u8; 32];
982
983        let msg1 = PartySignature::generate_signing_message(
984            &agreement_id,
985            &commitment,
986            &party_ref,
987            AgreementRole::Buyer,
988            &policy_id,
989        );
990
991        // Different role = different message
992        let msg2 = PartySignature::generate_signing_message(
993            &agreement_id,
994            &commitment,
995            &party_ref,
996            AgreementRole::Seller,
997            &policy_id,
998        );
999
1000        assert_ne!(msg1, msg2);
1001    }
1002
1003    #[test]
1004    fn test_attestation_id_generation() {
1005        let issuer = Address::new([1u8; 20]);
1006        let target = AttestationTarget::Agreement([2u8; 32]);
1007        let nonce = [3u8; 32];
1008
1009        let id = AttestationPacket::generate_id(
1010            &issuer,
1011            &target,
1012            AttestationType::Notarization,
1013            &nonce,
1014        );
1015        assert_ne!(id, [0u8; 32]);
1016    }
1017
1018    #[test]
1019    fn test_ip_scope_commitment() {
1020        let commitment = IpRightsAction::generate_scope_commitment(
1021            "US",
1022            "software",
1023            Some(31536000),
1024            None,
1025        );
1026        assert_ne!(commitment, [0u8; 32]);
1027    }
1028
1029    #[test]
1030    fn test_executor_terms_commitment() {
1031        let terms = b"escrow terms here";
1032        let interface = [1u8; 32];
1033
1034        let commitment = ExecutorLink::generate_terms_commitment(terms, &interface);
1035        assert_ne!(commitment, [0u8; 32]);
1036    }
1037}