Source Code

;; privacy-manager - Clarity 4
;; Comprehensive privacy control system for genomic data

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-POLICY-NOT-FOUND (err u101))
(define-constant ERR-INVALID-LEVEL (err u102))
(define-constant ERR-CONSENT-REQUIRED (err u103))
(define-constant ERR-ALREADY-EXISTS (err u104))

(define-constant PRIVACY-LEVEL-PUBLIC u0)
(define-constant PRIVACY-LEVEL-RESTRICTED u1)
(define-constant PRIVACY-LEVEL-CONFIDENTIAL u2)
(define-constant PRIVACY-LEVEL-HIGHLY-CONFIDENTIAL u3)

(define-map privacy-policies uint
  {
    data-owner: principal,
    privacy-level: uint,
    allow-research: bool,
    allow-commercial: bool,
    allow-third-party: bool,
    anonymization-required: bool,
    created-at: uint,
    updated-at: uint
  }
)

(define-map consent-records { policy-id: uint, requester: principal }
  {
    purpose: (string-ascii 100),
    granted: bool,
    granted-at: uint,
    expires-at: uint
  }
)

(define-map data-access-logs uint
  {
    policy-id: uint,
    accessor: principal,
    access-type: (string-ascii 50),
    timestamp: uint,
    approved: bool
  }
)

(define-map anonymization-settings uint
  {
    policy-id: uint,
    remove-identifiers: bool,
    hash-sensitive-fields: bool,
    generalize-locations: bool,
    date-perturbation: bool
  }
)

(define-data-var policy-counter uint u0)
(define-data-var log-counter uint u0)

;; Create privacy policy
(define-public (create-privacy-policy
    (privacy-level uint)
    (allow-research bool)
    (allow-commercial bool)
    (allow-third-party bool)
    (anonymization-required bool))
  (let ((policy-id (+ (var-get policy-counter) u1)))
    (asserts! (<= privacy-level PRIVACY-LEVEL-HIGHLY-CONFIDENTIAL) ERR-INVALID-LEVEL)
    (map-set privacy-policies policy-id
      {
        data-owner: tx-sender,
        privacy-level: privacy-level,
        allow-research: allow-research,
        allow-commercial: allow-commercial,
        allow-third-party: allow-third-party,
        anonymization-required: anonymization-required,
        created-at: stacks-block-time,
        updated-at: stacks-block-time
      })
    (var-set policy-counter policy-id)
    (ok policy-id)))

;; Update privacy level
(define-public (update-privacy-level (policy-id uint) (new-level uint))
  (let ((policy (unwrap! (map-get? privacy-policies policy-id) ERR-POLICY-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get data-owner policy)) ERR-NOT-AUTHORIZED)
    (asserts! (<= new-level PRIVACY-LEVEL-HIGHLY-CONFIDENTIAL) ERR-INVALID-LEVEL)
    (ok (map-set privacy-policies policy-id
      (merge policy { privacy-level: new-level, updated-at: stacks-block-time })))))

;; Grant consent for data access
(define-public (grant-consent
    (policy-id uint)
    (requester principal)
    (purpose (string-ascii 100))
    (duration uint))
  (let ((policy (unwrap! (map-get? privacy-policies policy-id) ERR-POLICY-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get data-owner policy)) ERR-NOT-AUTHORIZED)
    (ok (map-set consent-records { policy-id: policy-id, requester: requester }
      {
        purpose: purpose,
        granted: true,
        granted-at: stacks-block-time,
        expires-at: (+ stacks-block-time duration)
      }))))

;; Revoke consent
(define-public (revoke-consent (policy-id uint) (requester principal))
  (let ((policy (unwrap! (map-get? privacy-policies policy-id) ERR-POLICY-NOT-FOUND))
        (consent (unwrap! (map-get? consent-records { policy-id: policy-id, requester: requester })
                         ERR-POLICY-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get data-owner policy)) ERR-NOT-AUTHORIZED)
    (ok (map-set consent-records { policy-id: policy-id, requester: requester }
      (merge consent { granted: false })))))

;; Request data access
(define-public (request-access (policy-id uint) (access-type (string-ascii 50)))
  (let ((policy (unwrap! (map-get? privacy-policies policy-id) ERR-POLICY-NOT-FOUND))
        (consent (map-get? consent-records { policy-id: policy-id, requester: tx-sender })))
    (let ((has-consent (match consent
                         record (and (get granted record) (< stacks-block-time (get expires-at record)))
                         false)))
      (log-access policy-id tx-sender access-type has-consent)
      (if has-consent
        (ok true)
        ERR-CONSENT-REQUIRED))))

;; Configure anonymization settings
(define-public (set-anonymization-settings
    (policy-id uint)
    (remove-identifiers bool)
    (hash-sensitive-fields bool)
    (generalize-locations bool)
    (date-perturbation bool))
  (let ((policy (unwrap! (map-get? privacy-policies policy-id) ERR-POLICY-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get data-owner policy)) ERR-NOT-AUTHORIZED)
    (ok (map-set anonymization-settings policy-id
      {
        policy-id: policy-id,
        remove-identifiers: remove-identifiers,
        hash-sensitive-fields: hash-sensitive-fields,
        generalize-locations: generalize-locations,
        date-perturbation: date-perturbation
      }))))

;; Log access attempt
(define-private (log-access (policy-id uint) (accessor principal) (access-type (string-ascii 50)) (approved bool))
  (let ((log-id (+ (var-get log-counter) u1)))
    (map-set data-access-logs log-id
      {
        policy-id: policy-id,
        accessor: accessor,
        access-type: access-type,
        timestamp: stacks-block-time,
        approved: approved
      })
    (var-set log-counter log-id)
    true))

;; Read-only functions
(define-read-only (get-privacy-policy (policy-id uint))
  (ok (map-get? privacy-policies policy-id)))

(define-read-only (get-consent (policy-id uint) (requester principal))
  (ok (map-get? consent-records { policy-id: policy-id, requester: requester })))

(define-read-only (get-access-log (log-id uint))
  (ok (map-get? data-access-logs log-id)))

(define-read-only (get-anonymization-settings (policy-id uint))
  (ok (map-get? anonymization-settings policy-id)))

(define-read-only (check-access-permission (policy-id uint) (requester principal))
  (let ((consent (map-get? consent-records { policy-id: policy-id, requester: requester })))
    (ok (match consent
          record (and (get granted record) (< stacks-block-time (get expires-at record)))
          false))))

;; Clarity 4: principal-destruct?
(define-read-only (validate-owner (owner principal))
  (principal-destruct? owner))

;; Clarity 4: int-to-ascii
(define-read-only (format-policy-id (policy-id uint))
  (ok (int-to-ascii policy-id)))

;; Clarity 4: string-to-uint?
(define-read-only (parse-policy-id (id-str (string-ascii 20)))
  (string-to-uint? id-str))

;; Clarity 4: burn-block-height
(define-read-only (get-bitcoin-block)
  (ok burn-block-height))

Functions (16)

FunctionAccessArgs
create-privacy-policypublicprivacy-level: uint, allow-research: bool, allow-commercial: bool, allow-third-party: bool, anonymization-required: bool
update-privacy-levelpublicpolicy-id: uint, new-level: uint
grant-consentpublicpolicy-id: uint, requester: principal, purpose: (string-ascii 100
revoke-consentpublicpolicy-id: uint, requester: principal
request-accesspublicpolicy-id: uint, access-type: (string-ascii 50
set-anonymization-settingspublicpolicy-id: uint, remove-identifiers: bool, hash-sensitive-fields: bool, generalize-locations: bool, date-perturbation: bool
log-accessprivatepolicy-id: uint, accessor: principal, access-type: (string-ascii 50
get-privacy-policyread-onlypolicy-id: uint
get-consentread-onlypolicy-id: uint, requester: principal
get-access-logread-onlylog-id: uint
get-anonymization-settingsread-onlypolicy-id: uint
check-access-permissionread-onlypolicy-id: uint, requester: principal
validate-ownerread-onlyowner: principal
format-policy-idread-onlypolicy-id: uint
parse-policy-idread-onlyid-str: (string-ascii 20
get-bitcoin-blockread-only