Source Code

;; title: stx-proCredentials
;; version:
;; summary:
;; description:

;; title: resume-registry
;; version:
;; summary:
;; description:

;; Professional Credentials Verification Contract
;; A decentralized system for verifying and managing professional credentials
;; including education degrees, employment history, and skill certifications.
;; Organizations can issue verifications, and individuals maintain verified profiles
;; that employers and recruiters can trust for hiring decisions.

;; Contract owner principal stored at deployment
(define-constant contract-owner tx-sender)

;; Error codes for various failure scenarios
(define-constant ERR-OWNER-ONLY (err u100))
(define-constant ERR-NOT-FOUND (err u101))
(define-constant ERR-UNAUTHORIZED-ACCESS (err u102))
(define-constant ERR-ALREADY-EXISTS (err u103))
(define-constant ERR-INVALID-DATA (err u104))
(define-constant ERR-CREDENTIAL-EXPIRED (err u105))
(define-constant ERR-INVALID-INPUT (err u106))

;; Sequence counters for generating unique credential identifiers
(define-data-var education-credential-counter uint u0)
(define-data-var employment-credential-counter uint u0)
(define-data-var skill-credential-counter uint u0)

;; Storage for registered organizations that can issue verifications
;; Maps organization identifier to organization details
(define-map registered-organizations
  { organization-identifier: (string-ascii 64) }
  {
    organization-name: (string-ascii 100),
    organization-domain: (string-ascii 64),
    is-verified: bool,
    authorized-principal: principal,
  }
)

;; Storage for individual user profiles
;; Maps user principal to their profile information
(define-map user-profiles
  { user-address: principal }
  {
    full-name: (string-ascii 100),
    email-address: (string-ascii 100),
    profile-metadata-uri: (optional (string-utf8 256)),
    profile-created-at: uint,
    profile-updated-at: uint,
  }
)

;; Storage for education credentials
;; Maps combination of user address and credential ID to education details
(define-map education-records
  {
    holder-address: principal,
    record-identifier: (string-ascii 64),
  }
  {
    issuing-institution-id: (string-ascii 64),
    degree-title: (string-ascii 100),
    study-field: (string-ascii 100),
    education-start-date: uint,
    education-end-date: uint,
    is-verified: bool,
    verified-by: (optional principal),
    verified-at: (optional uint),
    additional-metadata-uri: (optional (string-utf8 256)),
  }
)

;; Storage for employment credentials
;; Maps combination of user address and credential ID to employment details
(define-map employment-records
  {
    holder-address: principal,
    record-identifier: (string-ascii 64),
  }
  {
    employer-organization-id: (string-ascii 64),
    job-title: (string-ascii 100),
    job-description: (string-utf8 500),
    employment-start-date: uint,
    employment-end-date: (optional uint),
    is-verified: bool,
    verified-by: (optional principal),
    verified-at: (optional uint),
    additional-metadata-uri: (optional (string-utf8 256)),
  }
)

;; Storage for skill and certification credentials
;; Maps combination of user address and credential ID to skill details
(define-map skill-records
  {
    holder-address: principal,
    record-identifier: (string-ascii 64),
  }
  {
    skill-title: (string-ascii 100),
    certification-issuer: (optional (string-ascii 64)),
    certification-issue-date: uint,
    certification-expiry-date: (optional uint),
    is-verified: bool,
    verified-by: (optional principal),
    verified-at: (optional uint),
    additional-metadata-uri: (optional (string-utf8 256)),
  }
)

;; Validates that organization identifier meets length requirements
(define-private (validate-organization-id (org-id (string-ascii 64)))
  (and
    (>= (len org-id) u1)
    (<= (len org-id) u64)
  )
)

;; Validates that credential identifier meets length requirements
(define-private (validate-record-id (record-id (string-ascii 64)))
  (and
    (>= (len record-id) u1)
    (<= (len record-id) u64)
  )
)

;; Checks if the transaction sender is the contract owner
(define-private (check-is-contract-owner)
  (is-eq tx-sender contract-owner)
)

;; Checks if the transaction sender is authorized for a specific organization
(define-private (check-is-organization-authorized (org-id (string-ascii 64)))
  (if (validate-organization-id org-id)
    (match (map-get? registered-organizations { organization-identifier: org-id })
      organization-data (is-eq tx-sender (get authorized-principal organization-data))
      false
    )
    false
  )
)

;; Checks if transaction sender has verification rights for an organization
;; Either contract owner or organization's authorized principal
(define-private (check-verification-authorization (org-id (string-ascii 64)))
  (if (validate-organization-id org-id)
    (or (check-is-contract-owner) (check-is-organization-authorized org-id))
    false
  )
)

;; Checks if a user profile exists for given address
(define-private (check-profile-exists (user-addr principal))
  (is-some (map-get? user-profiles { user-address: user-addr }))
)

;; Checks if an organization is registered
(define-private (check-organization-exists (org-id (string-ascii 64)))
  (if (validate-organization-id org-id)
    (is-some (map-get? registered-organizations { organization-identifier: org-id }))
    false
  )
)

;; Checks if an education record exists
(define-private (check-education-record-exists
    (holder-addr principal)
    (record-id (string-ascii 64))
  )
  (if (validate-record-id record-id)
    (is-some (map-get? education-records {
      holder-address: holder-addr,
      record-identifier: record-id,
    }))
    false
  )
)

;; Checks if an employment record exists
(define-private (check-employment-record-exists
    (holder-addr principal)
    (record-id (string-ascii 64))
  )
  (if (validate-record-id record-id)
    (is-some (map-get? employment-records {
      holder-address: holder-addr,
      record-identifier: record-id,
    }))
    false
  )
)

;; Checks if a skill record exists
(define-private (check-skill-record-exists
    (holder-addr principal)
    (record-id (string-ascii 64))
  )
  (if (validate-record-id record-id)
    (is-some (map-get? skill-records {
      holder-address: holder-addr,
      record-identifier: record-id,
    }))
    false
  )
)

;; Registers a new organization that can issue credential verifications
;; Organization starts as unverified and must be verified by contract owner
(define-public (register-organization
    (org-id (string-ascii 64))
    (name (string-ascii 100))
    (domain (string-ascii 64))
  )
  (begin
    (asserts! (validate-organization-id org-id) ERR-INVALID-INPUT)
    (asserts! (>= (len name) u1) ERR-INVALID-INPUT)
    (asserts! (>= (len domain) u1) ERR-INVALID-INPUT)

    (let ((organization-data {
        organization-name: name,
        organization-domain: domain,
        is-verified: false,
        authorized-principal: tx-sender,
      }))
      (if (check-organization-exists org-id)
        ERR-ALREADY-EXISTS
        (ok (map-set registered-organizations { organization-identifier: org-id }
          organization-data
        ))
      )
    )
  )
)

;; Verifies an organization, allowing them to issue credential verifications
;; Only contract owner can verify organizations
(define-public (verify-organization (org-id (string-ascii 64)))
  (begin
    (asserts! (validate-organization-id org-id) ERR-INVALID-INPUT)
    (asserts! (check-is-contract-owner) ERR-OWNER-ONLY)

    (match (map-get? registered-organizations { organization-identifier: org-id })
      organization-data (ok (map-set registered-organizations { organization-identifier: org-id }
        (merge organization-data { is-verified: true })
      ))
      ERR-NOT-FOUND
    )
  )
)

;; Updates organization information
;; Only the organization's authorized principal can update their info
(define-public (update-organization
    (org-id (string-ascii 64))
    (name (string-ascii 100))
    (domain (string-ascii 64))
  )
  (begin
    (asserts! (validate-organization-id org-id) ERR-INVALID-INPUT)
    (asserts! (>= (len name) u1) ERR-INVALID-INPUT)
    (asserts! (>= (len domain) u1) ERR-INVALID-INPUT)
    (asserts! (check-is-organization-authorized org-id) ERR-UNAUTHORIZED-ACCESS)

    (match (map-get? registered-organizations { organization-identifier: org-id })
      organization-data (ok (map-set registered-organizations { organization-identifier: org-id }
        (merge organization-data {
          organization-name: name,
          organization-domain: domain,
        })
      ))
      ERR-NOT-FOUND
    )
  )
)

;; Adds an education credential to user's profile
;; User must have a profile before adding credentials
(define-public (add-education-credential
    (institution-id (string-ascii 64))
    (degree (string-ascii 100))
    (field-of-study (string-ascii 100))
    (start-date uint)
    (end-date uint)
    (metadata-uri (optional (string-utf8 256)))
  )
  (begin
    (asserts! (validate-organization-id institution-id) ERR-INVALID-INPUT)
    (asserts! (>= (len degree) u1) ERR-INVALID-INPUT)
    (asserts! (>= (len field-of-study) u1) ERR-INVALID-INPUT)
    (asserts! (check-profile-exists tx-sender) ERR-NOT-FOUND)
    (asserts! (<= start-date end-date) ERR-INVALID-DATA)

    (let (
        (current-counter (var-get education-credential-counter))
        (generated-record-id (unwrap-panic (as-max-len?
          (concat "EDU-"
            (concat (unwrap-panic (as-max-len? institution-id u20))
              (concat "-" (unwrap-panic (as-max-len? degree u8)))
            ))
          u64
        )))
        (education-data {
          issuing-institution-id: institution-id,
          degree-title: degree,
          study-field: field-of-study,
          education-start-date: start-date,
          education-end-date: end-date,
          is-verified: false,
          verified-by: none,
          verified-at: none,
          additional-metadata-uri: metadata-uri,
        })
      )
      (var-set education-credential-counter (+ current-counter u1))
      (asserts! (validate-record-id generated-record-id) ERR-INVALID-INPUT)

      (ok (map-set education-records {
        holder-address: tx-sender,
        record-identifier: generated-record-id,
      }
        education-data
      ))
    )
  )
)

;; Adds an employment credential to user's profile
;; End date is optional for current employment
(define-public (add-employment-credential
    (organization-id (string-ascii 64))
    (title (string-ascii 100))
    (description (string-utf8 500))
    (start-date uint)
    (end-date (optional uint))
    (metadata-uri (optional (string-utf8 256)))
  )
  (begin
    (asserts! (validate-organization-id organization-id) ERR-INVALID-INPUT)
    (asserts! (>= (len title) u1) ERR-INVALID-INPUT)
    (asserts! (>= (len description) u1) ERR-INVALID-INPUT)
    (asserts! (check-profile-exists tx-sender) ERR-NOT-FOUND)

    (match end-date
      end-timestamp (asserts! (<= start-date end-timestamp) ERR-INVALID-DATA)
      true
    )

    (let (
        (current-counter (var-get employment-credential-counter))
        (generated-record-id (unwrap-panic (as-max-len?
          (concat "EMP-"
            (concat (unwrap-panic (as-max-len? organization-id u20))
              (concat "-" (unwrap-panic (as-max-len? title u8)))
            ))
          u64
        )))
        (employment-data {
          employer-organization-id: organization-id,
          job-title: title,
          job-description: description,
          employment-start-date: start-date,
          employment-end-date: end-date,
          is-verified: false,
          verified-by: none,
          verified-at: none,
          additional-metadata-uri: metadata-uri,
        })
      )
      (var-set employment-credential-counter (+ current-counter u1))
      (asserts! (validate-record-id generated-record-id) ERR-INVALID-INPUT)

      (ok (map-set employment-records {
        holder-address: tx-sender,
        record-identifier: generated-record-id,
      }
        employment-data
      ))
    )
  )
)

;; Adds a skill or certification credential to user's profile
;; Expiry date is optional for skills that don't expire
(define-public (add-skill-credential
    (skill-name (string-ascii 100))
    (issuer (optional (string-ascii 64)))
    (issue-date uint)
    (expiry-date (optional uint))
    (metadata-uri (optional (string-utf8 256)))
  )
  (begin
    (asserts! (>= (len skill-name) u1) ERR-INVALID-INPUT)

    (match issuer
      issuer-organization-id (begin
        (asserts! (validate-organization-id issuer-organization-id)
          ERR-INVALID-INPUT
        )
        (asserts! (check-organization-exists issuer-organization-id)
          ERR-NOT-FOUND
        )
      )
      true
    )

    (asserts! (check-profile-exists tx-sender) ERR-NOT-FOUND)

    (match expiry-date
      expiry-timestamp (asserts! (<= issue-date expiry-timestamp) ERR-INVALID-DATA)
      true
    )

    (let (
        (current-counter (var-get skill-credential-counter))
        (issuer-substring (match issuer
          some-issuer-id (unwrap-panic (as-max-len? some-issuer-id u15))
          "none"
        ))
        (generated-record-id (unwrap-panic (as-max-len?
          (concat "SKILL-"
            (concat issuer-substring
              (concat "-" (unwrap-panic (as-max-len? skill-name u15)))
            ))
          u64
        )))
        (skill-data {
          skill-title: skill-name,
          certification-issuer: issuer,
          certification-issue-date: issue-date,
          certification-expiry-date: expiry-date,
          is-verified: false,
          verified-by: none,
          verified-at: none,
          additional-metadata-uri: metadata-uri,
        })
      )
      (var-set skill-credential-counter (+ current-counter u1))
      (asserts! (validate-record-id generated-record-id) ERR-INVALID-INPUT)

      (ok (map-set skill-records {
        holder-address: tx-sender,
        record-identifier: generated-record-id,
      }
        skill-data
      ))
    )
  )
)

;; Revokes an education credential verification
;; Only issuing organization or contract owner can revoke
(define-public (revoke-education-verification
    (profile-address principal)
    (credential-id (string-ascii 64))
    (institution-id (string-ascii 64))
  )
  (begin
    (asserts! (validate-organization-id institution-id) ERR-INVALID-INPUT)
    (asserts! (validate-record-id credential-id) ERR-INVALID-INPUT)
    (asserts!
      (or (check-is-contract-owner) (check-is-organization-authorized institution-id))
      ERR-UNAUTHORIZED-ACCESS
    )
    (asserts! (check-education-record-exists profile-address credential-id)
      ERR-NOT-FOUND
    )

    (match (map-get? education-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
      education-record (begin
        (asserts!
          (is-eq (get issuing-institution-id education-record) institution-id)
          ERR-UNAUTHORIZED-ACCESS
        )
        (asserts! (get is-verified education-record) ERR-INVALID-DATA)
        (ok (map-set education-records {
          holder-address: profile-address,
          record-identifier: credential-id,
        }
          (merge education-record {
            is-verified: false,
            verified-by: none,
            verified-at: none,
          })
        ))
      )
      ERR-NOT-FOUND
    )
  )
)

;; Revokes an employment credential verification
;; Only issuing organization or contract owner can revoke
(define-public (revoke-employment-verification
    (profile-address principal)
    (credential-id (string-ascii 64))
    (organization-id (string-ascii 64))
  )
  (begin
    (asserts! (validate-organization-id organization-id) ERR-INVALID-INPUT)
    (asserts! (validate-record-id credential-id) ERR-INVALID-INPUT)
    (asserts!
      (or (check-is-contract-owner) (check-is-organization-authorized organization-id))
      ERR-UNAUTHORIZED-ACCESS
    )
    (asserts! (check-employment-record-exists profile-address credential-id)
      ERR-NOT-FOUND
    )

    (match (map-get? employment-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
      employment-record (begin
        (asserts!
          (is-eq (get employer-organization-id employment-record) organization-id)
          ERR-UNAUTHORIZED-ACCESS
        )
        (asserts! (get is-verified employment-record) ERR-INVALID-DATA)
        (ok (map-set employment-records {
          holder-address: profile-address,
          record-identifier: credential-id,
        }
          (merge employment-record {
            is-verified: false,
            verified-by: none,
            verified-at: none,
          })
        ))
      )
      ERR-NOT-FOUND
    )
  )
)

;; Revokes a skill credential verification
;; Only issuing organization or contract owner can revoke
(define-public (revoke-skill-verification
    (profile-address principal)
    (credential-id (string-ascii 64))
    (org-id (string-ascii 64))
  )
  (begin
    (asserts! (validate-organization-id org-id) ERR-INVALID-INPUT)
    (asserts! (validate-record-id credential-id) ERR-INVALID-INPUT)
    (asserts!
      (or (check-is-contract-owner) (check-is-organization-authorized org-id))
      ERR-UNAUTHORIZED-ACCESS
    )
    (asserts! (check-skill-record-exists profile-address credential-id)
      ERR-NOT-FOUND
    )

    (match (map-get? skill-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
      skill-record (begin
        (match (get certification-issuer skill-record)
          issuer-organization-id (asserts! (is-eq issuer-organization-id org-id) ERR-UNAUTHORIZED-ACCESS)
          true
        )
        (asserts! (get is-verified skill-record) ERR-INVALID-DATA)
        (ok (map-set skill-records {
          holder-address: profile-address,
          record-identifier: credential-id,
        }
          (merge skill-record {
            is-verified: false,
            verified-by: none,
            verified-at: none,
          })
        ))
      )
      ERR-NOT-FOUND
    )
  )
)

;; Retrieves profile information for a given address
;; Returns none if profile doesn't exist
(define-read-only (get-profile (address principal))
  (map-get? user-profiles { user-address: address })
)

;; Retrieves organization information by organization ID
;; Returns none if organization doesn't exist
(define-read-only (get-organization (org-id (string-ascii 64)))
  (if (validate-organization-id org-id)
    (map-get? registered-organizations { organization-identifier: org-id })
    none
  )
)

;; Retrieves education credential details
;; Returns none if credential doesn't exist
(define-read-only (get-education-credential
    (profile-address principal)
    (credential-id (string-ascii 64))
  )
  (if (validate-record-id credential-id)
    (map-get? education-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
    none
  )
)

;; Retrieves employment credential details
;; Returns none if credential doesn't exist
(define-read-only (get-employment-credential
    (profile-address principal)
    (credential-id (string-ascii 64))
  )
  (if (validate-record-id credential-id)
    (map-get? employment-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
    none
  )
)

;; Retrieves skill credential details
;; Returns none if credential doesn't exist
(define-read-only (get-skill-credential
    (profile-address principal)
    (credential-id (string-ascii 64))
  )
  (if (validate-record-id credential-id)
    (map-get? skill-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
    none
  )
)

;; Checks if an education credential is currently valid
;; Returns true only if verified
(define-read-only (is-education-credential-valid
    (profile-address principal)
    (credential-id (string-ascii 64))
  )
  (if (validate-record-id credential-id)
    (match (map-get? education-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
      education-record (get is-verified education-record)
      false
    )
    false
  )
)

;; Checks if an employment credential is currently valid
;; Returns true only if verified
(define-read-only (is-employment-credential-valid
    (profile-address principal)
    (credential-id (string-ascii 64))
  )
  (if (validate-record-id credential-id)
    (match (map-get? employment-records {
      holder-address: profile-address,
      record-identifier: credential-id,
    })
      employment-record (get is-verified employment-record)
      false
    )
    false
  )
)

Functions (26)

FunctionAccessArgs
validate-organization-idprivateorg-id: (string-ascii 64
validate-record-idprivaterecord-id: (string-ascii 64
check-is-contract-ownerprivate
check-is-organization-authorizedprivateorg-id: (string-ascii 64
check-verification-authorizationprivateorg-id: (string-ascii 64
check-profile-existsprivateuser-addr: principal
check-organization-existsprivateorg-id: (string-ascii 64
check-education-record-existsprivateholder-addr: principal, record-id: (string-ascii 64
check-employment-record-existsprivateholder-addr: principal, record-id: (string-ascii 64
check-skill-record-existsprivateholder-addr: principal, record-id: (string-ascii 64
register-organizationpublicorg-id: (string-ascii 64
verify-organizationpublicorg-id: (string-ascii 64
update-organizationpublicorg-id: (string-ascii 64
add-education-credentialpublicinstitution-id: (string-ascii 64
add-employment-credentialpublicorganization-id: (string-ascii 64
add-skill-credentialpublicskill-name: (string-ascii 100
revoke-education-verificationpublicprofile-address: principal, credential-id: (string-ascii 64
revoke-employment-verificationpublicprofile-address: principal, credential-id: (string-ascii 64
revoke-skill-verificationpublicprofile-address: principal, credential-id: (string-ascii 64
get-profileread-onlyaddress: principal
get-organizationread-onlyorg-id: (string-ascii 64
get-education-credentialread-onlyprofile-address: principal, credential-id: (string-ascii 64
get-employment-credentialread-onlyprofile-address: principal, credential-id: (string-ascii 64
get-skill-credentialread-onlyprofile-address: principal, credential-id: (string-ascii 64
is-education-credential-validread-onlyprofile-address: principal, credential-id: (string-ascii 64
is-employment-credential-validread-onlyprofile-address: principal, credential-id: (string-ascii 64