Source Code

;; ClearSight - Supply Chain Transparency Contract
;; Enhanced with RBAC, Audit Trail, Advanced Status Management, and Input Validation

;; title: source
;; version:
;; summary:
;; description:
;; Constants for validation
(define-constant MAX-PRODUCT-ID-LENGTH u36)
(define-constant MAX-LOCATION-LENGTH u50)
(define-constant MAX-REASON-LENGTH u50)

;; traits
;;
;; Enhanced status constants
(define-constant STATUS-REGISTERED "registered")
(define-constant STATUS-IN-TRANSIT "in-transit")
(define-constant STATUS-DELIVERED "delivered")
(define-constant STATUS-TRANSFERRED "transferred")
(define-constant STATUS-VERIFIED "verified")
(define-constant STATUS-RETURNED "returned")
(define-constant STATUS-REJECTED "rejected")

;; token definitions
;;
;; Action constants (all 12 chars or less)
(define-constant ACTION-UPDATE "status-upd")
(define-constant ACTION-REGISTER "register")
(define-constant ACTION-TRANSFER "transfer")

;; constants
;;
;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-PRODUCT-EXISTS (err u101))
(define-constant ERR-PRODUCT-NOT-FOUND (err u102))
(define-constant ERR-INVALID-STATUS (err u103))
(define-constant ERR-INVALID-PRODUCT-ID (err u104))
(define-constant ERR-INVALID-LOCATION (err u105))
(define-constant ERR-INVALID-OWNER (err u106))
(define-constant ERR-ROLE-EXISTS (err u107))
(define-constant ERR-INVALID-ROLE (err u108))
(define-constant ERR-INVALID-STATUS-TRANSITION (err u109))
(define-constant ERR-INVALID-REASON (err u110))

;; data vars
;;
;; Define roles
(define-constant ROLE-ADMIN u1)
(define-constant ROLE-MANUFACTURER u2)
(define-constant ROLE-DISTRIBUTOR u3)
(define-constant ROLE-RETAILER u4)

;; data maps
;;
;; Define data variables
(define-data-var contract-owner principal tx-sender)

;; public functions
;;
;; Role management
(define-map user-roles
    { user: principal }
    { role: uint }
)

;; read only functions
;;
;; Enhanced products map with status tracking
(define-map products 
    { product-id: (string-ascii 36) }
    { 
        manufacturer: principal,
        timestamp: uint,
        current-owner: principal,
        current-status: (string-ascii 12),
        verified: bool,
        status-update-count: uint
    }
)

;; private functions
;;
;; Status history map
(define-map status-history
    { 
        product-id: (string-ascii 36),
        update-number: uint
    }
    {
        status: (string-ascii 12),
        timestamp: uint,
        changed-by: principal,
        reason: (string-ascii 50),
        location: (string-ascii 50)
    }
)

;; Product history
(define-map product-history
    { 
        product-id: (string-ascii 36),
        timestamp: uint
    }
    {
        owner: principal,
        action: (string-ascii 12),
        location: (string-ascii 50),
        previous-owner: (optional principal)
    }
)

;; Audit trail
(define-map audit-log
    { 
        transaction-id: uint,
        timestamp: uint
    }
    {
        actor: principal,
        action: (string-ascii 12),
        product-id: (string-ascii 36),
        details: (string-ascii 50)
    }
)

(define-data-var audit-counter uint u0)

;; Enhanced validation functions
(define-private (is-valid-product-id (product-id (string-ascii 36)))
    (and
        (>= (len product-id) u1)
        (<= (len product-id) MAX-PRODUCT-ID-LENGTH)
        (not (is-eq product-id ""))
        (not (is-eq product-id " "))
        true
    )
)

(define-private (is-valid-location (location (string-ascii 50)))
    (and
        (>= (len location) u1)
        (<= (len location) MAX-LOCATION-LENGTH)
        (not (is-eq location ""))
        (not (is-eq location " "))
        true
    )
)

(define-private (is-valid-reason (reason (string-ascii 50)))
    (and
        (>= (len reason) u1)
        (<= (len reason) MAX-REASON-LENGTH)
        (not (is-eq reason ""))
        (not (is-eq reason " "))
        true
    )
)

;; Status validation functions
(define-private (is-valid-status (status (string-ascii 12)))
    (or 
        (is-eq status STATUS-REGISTERED)
        (is-eq status STATUS-IN-TRANSIT)
        (is-eq status STATUS-DELIVERED)
        (is-eq status STATUS-TRANSFERRED)
        (is-eq status STATUS-VERIFIED)
        (is-eq status STATUS-RETURNED)
        (is-eq status STATUS-REJECTED)
    )
)

(define-private (is-valid-status-transition (current-status (string-ascii 12)) (new-status (string-ascii 12)))
    (or
        (and (is-eq current-status STATUS-REGISTERED)
             (or (is-eq new-status STATUS-IN-TRANSIT)
                 (is-eq new-status STATUS-TRANSFERRED)))
        (and (is-eq current-status STATUS-IN-TRANSIT)
             (or (is-eq new-status STATUS-DELIVERED)
                 (is-eq new-status STATUS-RETURNED)))
        (and (is-eq current-status STATUS-DELIVERED)
             (or (is-eq new-status STATUS-VERIFIED)
                 (is-eq new-status STATUS-REJECTED)))
        (and (is-eq current-status STATUS-TRANSFERRED)
             (is-eq new-status STATUS-VERIFIED))
    )
)

;; Fixed authorization function with consistent return type
(define-private (check-authorization (user principal) (required-role uint))
    (match (map-get? user-roles { user: user })
        role-data (if (is-eq (get role role-data) required-role)
                     (ok true)
                     ERR-NOT-AUTHORIZED)
        ERR-NOT-AUTHORIZED
    )
)

(define-private (create-audit-log 
    (actor principal) 
    (action (string-ascii 12)) 
    (product-id (string-ascii 36)) 
    (details (string-ascii 50)))
    (begin
        (asserts! (is-valid-product-id product-id) ERR-INVALID-PRODUCT-ID)
        (let ((current-counter (var-get audit-counter)))
            (map-set audit-log
                { 
                    transaction-id: current-counter,
                    timestamp: stacks-block-height
                }
                {
                    actor: actor,
                    action: action,
                    product-id: product-id,
                    details: details
                }
            )
            (var-set audit-counter (+ current-counter u1))
            (ok true))
    )
)



;; Updated product management functions
(define-public (update-product-status
    (product-id (string-ascii 36))
    (new-status (string-ascii 12))
    (reason (string-ascii 50))
    (location (string-ascii 50)))
    (begin
        ;; Authorization check
        (try! (check-authorization tx-sender ROLE-MANUFACTURER))

        ;; Input validation
        (asserts! (is-valid-product-id product-id) ERR-INVALID-PRODUCT-ID)
        (asserts! (is-valid-status new-status) ERR-INVALID-STATUS)
        (asserts! (is-valid-reason reason) ERR-INVALID-REASON)
        (asserts! (is-valid-location location) ERR-INVALID-LOCATION)

        (let ((product (unwrap! (map-get? products { product-id: product-id }) ERR-PRODUCT-NOT-FOUND)))
            ;; Status transition validation
            (asserts! (is-valid-status-transition (get current-status product) new-status) 
                     ERR-INVALID-STATUS-TRANSITION)

            ;; Update product status
            (map-set products
                { product-id: product-id }
                (merge product { 
                    current-status: new-status,
                    status-update-count: (+ (get status-update-count product) u1)
                })
            )

            ;; Record in status history
            (map-set status-history
                { 
                    product-id: product-id,
                    update-number: (get status-update-count product)
                }
                {
                    status: new-status,
                    timestamp: stacks-block-height,
                    changed-by: tx-sender,
                    reason: reason,
                    location: location
                }
            )

            ;; Create audit log entry
            (try! (create-audit-log tx-sender ACTION-UPDATE product-id 
                                   (concat "Status: " new-status)))
            (ok true)
        )
    )
)

(define-public (register-product 
    (product-id (string-ascii 36))
    (location (string-ascii 50)))
    (begin
        ;; Authorization check
        (try! (check-authorization tx-sender ROLE-MANUFACTURER))

        ;; Input validation
        (asserts! (is-valid-product-id product-id) ERR-INVALID-PRODUCT-ID)
        (asserts! (is-valid-location location) ERR-INVALID-LOCATION)

        ;; Check if product already exists
        (asserts! (is-none (map-get? products { product-id: product-id })) ERR-PRODUCT-EXISTS)

        ;; Register new product
        (map-set products
            { product-id: product-id }
            {
                manufacturer: tx-sender,
                timestamp: stacks-block-height,
                current-owner: tx-sender,
                current-status: STATUS-REGISTERED,
                verified: true,
                status-update-count: u1
            }
        )

        ;; Initial status history entry
        (map-set status-history
            { product-id: product-id, update-number: u0 }
            {
                status: STATUS-REGISTERED,
                timestamp: stacks-block-height,
                changed-by: tx-sender,
                reason: "Initial registration",
                location: location
            }
        )

        ;; Create audit log entry
        (try! (create-audit-log tx-sender ACTION-REGISTER product-id "Product registered"))
        (ok true)
    )
)

;; Read-only functions
(define-read-only (get-status-history (product-id (string-ascii 36)) (update-number uint))
    (begin
        (asserts! (is-valid-product-id product-id) ERR-INVALID-PRODUCT-ID)
        (ok (map-get? status-history { product-id: product-id, update-number: update-number }))
    )
)

(define-read-only (get-product-details (product-id (string-ascii 36)))
    (begin
        (asserts! (is-valid-product-id product-id) ERR-INVALID-PRODUCT-ID)
        (ok (map-get? products { product-id: product-id }))
    )
)

;; Role management function
(define-public (assign-role (user principal) (role uint))
    (begin
        ;; Authorization check
        (try! (check-authorization tx-sender ROLE-ADMIN))

        ;; Validate role
        (asserts! (or (is-eq role ROLE-MANUFACTURER)
                     (is-eq role ROLE-DISTRIBUTOR)
                     (is-eq role ROLE-RETAILER)) 
                 ERR-INVALID-ROLE)

        ;; Assign role
        (map-set user-roles
            { user: user }
            { role: role }
        )
        (ok true)
    )
)

Functions (12)

FunctionAccessArgs
is-valid-product-idprivateproduct-id: (string-ascii 36
is-valid-locationprivatelocation: (string-ascii 50
is-valid-reasonprivatereason: (string-ascii 50
is-valid-statusprivatestatus: (string-ascii 12
is-valid-status-transitionprivatecurrent-status: (string-ascii 12
check-authorizationprivateuser: principal, required-role: uint
create-audit-logprivateactor: principal, action: (string-ascii 12
update-product-statuspublicproduct-id: (string-ascii 36
register-productpublicproduct-id: (string-ascii 36
get-status-historyread-onlyproduct-id: (string-ascii 36
get-product-detailsread-onlyproduct-id: (string-ascii 36
assign-rolepublicuser: principal, role: uint