From 5da17cd093ea657bb6b62191b0ed2736e35074c1 Mon Sep 17 00:00:00 2001 From: Adenike Akande <38114140-1adenikeakande@users.noreply.replit.com> Date: Wed, 1 Jan 2025 03:46:54 +0000 Subject: [PATCH 1/5] Proper data validation handling --- contracts/RightsRegistry.clar | 154 ++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 36 deletions(-) diff --git a/contracts/RightsRegistry.clar b/contracts/RightsRegistry.clar index 871e1d9..855f8ac 100644 --- a/contracts/RightsRegistry.clar +++ b/contracts/RightsRegistry.clar @@ -1,4 +1,4 @@ -;; First, let's define the SIP-010 trait +;; Define SIP-010 trait (define-trait sip-010-trait ( (transfer (uint principal principal (optional (buff 34))) (response bool uint)) @@ -11,8 +11,17 @@ ) ) -;; Rights Registry Contract -;; Handles music rights ownership, royalty splits, and rights transfers +;; Constants for validation +(define-constant ERR-NOT-AUTHORIZED (err u100)) +(define-constant ERR-INVALID-SONG (err u101)) +(define-constant ERR-ALREADY-EXISTS (err u102)) +(define-constant ERR-INVALID-SHARE (err u103)) +(define-constant ERR-INVALID-SONG-ID (err u104)) +(define-constant ERR-INVALID-TITLE (err u105)) +(define-constant ERR-INVALID-STATUS (err u106)) +(define-constant ERR-INVALID-ROLE (err u107)) +(define-constant ERR-ZERO-ADDRESS (err u108)) +(define-constant ERR-TOTAL-SHARE-EXCEEDED (err u109)) ;; Data Variables (define-data-var contract-owner principal tx-sender) @@ -36,28 +45,71 @@ } ) -;; Constants -(define-constant ERR-NOT-AUTHORIZED (err u100)) -(define-constant ERR-INVALID-SONG (err u101)) -(define-constant ERR-ALREADY-EXISTS (err u102)) -(define-constant ERR-INVALID-SHARE (err u103)) +(define-map total-song-shares + { song-id: uint } + { total-share: uint } +) + +;; Private Functions +(define-private (validate-song-id (song-id uint)) + (ok (asserts! (> song-id u0) ERR-INVALID-SONG-ID)) +) + +(define-private (validate-title (title (string-ascii 256))) + (ok (asserts! (not (is-eq title "")) ERR-INVALID-TITLE)) +) + +(define-private (validate-status (status (string-ascii 10))) + (ok (asserts! + (or (is-eq status "active") (is-eq status "inactive")) + ERR-INVALID-STATUS)) +) + +(define-private (validate-role (role (string-ascii 20))) + (ok (asserts! + (or + (is-eq role "writer") + (is-eq role "producer") + (is-eq role "performer") + ) + ERR-INVALID-ROLE)) +) + +(define-private (get-total-share (song-id uint)) + (default-to + { total-share: u0 } + (map-get? total-song-shares { song-id: song-id })) +) ;; Read-Only Functions (define-read-only (get-song-details (song-id uint)) - (map-get? rights-registry { song-id: song-id }) + (begin + (try! (validate-song-id song-id)) + (ok (map-get? rights-registry { song-id: song-id })) + ) ) (define-read-only (get-collaborator-share (song-id uint) (collaborator principal)) - (map-get? royalty-splits { song-id: song-id, collaborator: collaborator }) + (begin + (try! (validate-song-id song-id)) + (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })) + ) ) ;; Public Functions (define-public (register-song (song-id uint) (title (string-ascii 256))) - (let - ((song-exists (map-get? rights-registry { song-id: song-id }))) + (begin + ;; Input validation + (try! (validate-song-id song-id)) + (try! (validate-title title)) + + ;; Authorization check (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) - (asserts! (is-none song-exists) ERR-ALREADY-EXISTS) - + + ;; Check if song already exists + (asserts! (is-none (map-get? rights-registry { song-id: song-id })) ERR-ALREADY-EXISTS) + + ;; Register the song (map-set rights-registry { song-id: song-id } { @@ -67,6 +119,13 @@ status: "active" } ) + + ;; Initialize total share + (map-set total-song-shares + { song-id: song-id } + { total-share: u0 } + ) + (ok true) ) ) @@ -76,34 +135,56 @@ (collaborator principal) (share uint) (role (string-ascii 20))) - (let - ((song-exists (map-get? rights-registry { song-id: song-id }))) - - ;; Validations - (asserts! (is-some song-exists) ERR-INVALID-SONG) - (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - (asserts! (<= share u10000) ERR-INVALID-SHARE) ;; Max 100% - - (map-set royalty-splits - { song-id: song-id, collaborator: collaborator } - { share: share, role: role } + (begin + ;; Input validation + (try! (validate-song-id song-id)) + (try! (validate-role role)) + (asserts! (not (is-eq collaborator tx-sender)) ERR-ZERO-ADDRESS) + (asserts! (<= share u10000) ERR-INVALID-SHARE) + + ;; Get song details and validate + (let ((song-exists (map-get? rights-registry { song-id: song-id })) + (current-total (get total-share (get-total-share song-id)))) + + (asserts! (is-some song-exists) ERR-INVALID-SONG) + (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) + + ;; Check if total share would exceed 100% + (asserts! (<= (+ share current-total) u10000) ERR-TOTAL-SHARE-EXCEEDED) + + ;; Update collaborator share + (map-set royalty-splits + { song-id: song-id, collaborator: collaborator } + { share: share, role: role } + ) + + ;; Update total share + (map-set total-song-shares + { song-id: song-id } + { total-share: (+ share current-total) } + ) + + (ok true) ) - (ok true) ) ) (define-public (update-song-status (song-id uint) (new-status (string-ascii 10))) - (let - ((song-exists (map-get? rights-registry { song-id: song-id }))) - - (asserts! (is-some song-exists) ERR-INVALID-SONG) - (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - - (map-set rights-registry - { song-id: song-id } - (merge (unwrap-panic song-exists) { status: new-status }) + (begin + ;; Input validation + (try! (validate-song-id song-id)) + (try! (validate-status new-status)) + + (let ((song-exists (map-get? rights-registry { song-id: song-id }))) + (asserts! (is-some song-exists) ERR-INVALID-SONG) + (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) + + (map-set rights-registry + { song-id: song-id } + (merge (unwrap-panic song-exists) { status: new-status }) + ) + (ok true) ) - (ok true) ) ) @@ -111,6 +192,7 @@ (define-public (transfer-ownership (new-owner principal)) (begin (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) + (asserts! (not (is-eq new-owner tx-sender)) ERR-ZERO-ADDRESS) (var-set contract-owner new-owner) (ok true) ) From 365255dfc10b7d99ad330ee01b34ba2384378680 Mon Sep 17 00:00:00 2001 From: Adenike Akande <38114140-1adenikeakande@users.noreply.replit.com> Date: Wed, 1 Jan 2025 03:52:12 +0000 Subject: [PATCH 2/5] Better error handling --- contracts/RightsRegistry.clar | 184 +++++++++++++++++++--------------- 1 file changed, 103 insertions(+), 81 deletions(-) diff --git a/contracts/RightsRegistry.clar b/contracts/RightsRegistry.clar index 855f8ac..5f4f164 100644 --- a/contracts/RightsRegistry.clar +++ b/contracts/RightsRegistry.clar @@ -33,15 +33,15 @@ owner: principal, title: (string-ascii 256), created-at: uint, - status: (string-ascii 10) ;; "active" or "inactive" + status: (string-ascii 10) } ) (define-map royalty-splits { song-id: uint, collaborator: principal } { - share: uint, ;; Percentage * 100 (e.g., 2500 = 25%) - role: (string-ascii 20) ;; e.g., "writer", "producer", "performer" + share: uint, + role: (string-ascii 20) } ) @@ -51,142 +51,165 @@ ) ;; Private Functions -(define-private (validate-song-id (song-id uint)) - (ok (asserts! (> song-id u0) ERR-INVALID-SONG-ID)) -) - -(define-private (validate-title (title (string-ascii 256))) - (ok (asserts! (not (is-eq title "")) ERR-INVALID-TITLE)) -) - -(define-private (validate-status (status (string-ascii 10))) - (ok (asserts! - (or (is-eq status "active") (is-eq status "inactive")) +(define-private (sanitize-song-id (id uint)) + (if (> id u0) + id + u0)) + +(define-private (sanitize-title (input (string-ascii 256))) + (if (is-eq input "") + "untitled" + input)) + +(define-private (sanitize-status (input (string-ascii 10))) + (if (or (is-eq input "active") (is-eq input "inactive")) + input + "inactive")) + +(define-private (sanitize-role (input (string-ascii 20))) + (if (or + (is-eq input "writer") + (is-eq input "producer") + (is-eq input "performer")) + input + "other")) + +(define-private (validate-song-id (id uint)) + (if (> id u0) + (ok id) + ERR-INVALID-SONG-ID)) + +(define-private (validate-title (input (string-ascii 256))) + (if (not (is-eq input "")) + (ok input) + ERR-INVALID-TITLE)) + +(define-private (validate-status (input (string-ascii 10))) + (if (or (is-eq input "active") (is-eq input "inactive")) + (ok input) ERR-INVALID-STATUS)) -) -(define-private (validate-role (role (string-ascii 20))) - (ok (asserts! - (or - (is-eq role "writer") - (is-eq role "producer") - (is-eq role "performer") - ) +(define-private (validate-role (input (string-ascii 20))) + (if (or + (is-eq input "writer") + (is-eq input "producer") + (is-eq input "performer")) + (ok input) ERR-INVALID-ROLE)) -) (define-private (get-total-share (song-id uint)) - (default-to - { total-share: u0 } - (map-get? total-song-shares { song-id: song-id })) -) + (get total-share + (default-to + { total-share: u0 } + (map-get? total-song-shares { song-id: (sanitize-song-id song-id) })))) ;; Read-Only Functions (define-read-only (get-song-details (song-id uint)) - (begin - (try! (validate-song-id song-id)) - (ok (map-get? rights-registry { song-id: song-id })) - ) -) + (let + ((safe-id (sanitize-song-id song-id))) + (ok (map-get? rights-registry { song-id: safe-id })))) (define-read-only (get-collaborator-share (song-id uint) (collaborator principal)) - (begin - (try! (validate-song-id song-id)) - (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })) - ) -) + (let + ((safe-id (sanitize-song-id song-id))) + (ok (map-get? royalty-splits + { song-id: safe-id, collaborator: collaborator })))) ;; Public Functions (define-public (register-song (song-id uint) (title (string-ascii 256))) - (begin + (let + ((safe-id (sanitize-song-id song-id)) + (safe-title (sanitize-title title))) + ;; Input validation - (try! (validate-song-id song-id)) - (try! (validate-title title)) - + (try! (validate-song-id safe-id)) + (try! (validate-title safe-title)) + ;; Authorization check (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) - + ;; Check if song already exists - (asserts! (is-none (map-get? rights-registry { song-id: song-id })) ERR-ALREADY-EXISTS) - + (asserts! (is-none (map-get? rights-registry { song-id: safe-id })) ERR-ALREADY-EXISTS) + ;; Register the song (map-set rights-registry - { song-id: song-id } + { song-id: safe-id } { owner: tx-sender, - title: title, + title: safe-title, created-at: block-height, status: "active" } ) - + ;; Initialize total share (map-set total-song-shares - { song-id: song-id } + { song-id: safe-id } { total-share: u0 } ) - + (ok true) - ) -) + )) (define-public (add-collaborator (song-id uint) (collaborator principal) (share uint) (role (string-ascii 20))) - (begin + (let + ((safe-id (sanitize-song-id song-id)) + (safe-role (sanitize-role role))) + ;; Input validation - (try! (validate-song-id song-id)) - (try! (validate-role role)) + (try! (validate-song-id safe-id)) + (try! (validate-role safe-role)) (asserts! (not (is-eq collaborator tx-sender)) ERR-ZERO-ADDRESS) (asserts! (<= share u10000) ERR-INVALID-SHARE) - + ;; Get song details and validate - (let ((song-exists (map-get? rights-registry { song-id: song-id })) - (current-total (get total-share (get-total-share song-id)))) - + (let ((song-exists (map-get? rights-registry { song-id: safe-id })) + (current-total (get-total-share safe-id))) + (asserts! (is-some song-exists) ERR-INVALID-SONG) (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - + ;; Check if total share would exceed 100% (asserts! (<= (+ share current-total) u10000) ERR-TOTAL-SHARE-EXCEEDED) - + ;; Update collaborator share (map-set royalty-splits - { song-id: song-id, collaborator: collaborator } - { share: share, role: role } + { song-id: safe-id, collaborator: collaborator } + { share: share, role: safe-role } ) - + ;; Update total share (map-set total-song-shares - { song-id: song-id } + { song-id: safe-id } { total-share: (+ share current-total) } ) - + (ok true) - ) - ) -) + ))) (define-public (update-song-status (song-id uint) (new-status (string-ascii 10))) - (begin + (let + ((safe-id (sanitize-song-id song-id)) + (safe-status (sanitize-status new-status))) + ;; Input validation - (try! (validate-song-id song-id)) - (try! (validate-status new-status)) - - (let ((song-exists (map-get? rights-registry { song-id: song-id }))) + (try! (validate-song-id safe-id)) + (try! (validate-status safe-status)) + + (let ((song-exists (map-get? rights-registry { song-id: safe-id }))) (asserts! (is-some song-exists) ERR-INVALID-SONG) (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - + (map-set rights-registry - { song-id: song-id } - (merge (unwrap-panic song-exists) { status: new-status }) + { song-id: safe-id } + (merge (unwrap-panic song-exists) { status: safe-status }) ) (ok true) - ) - ) -) + ))) ;; Administrative Functions (define-public (transfer-ownership (new-owner principal)) @@ -195,5 +218,4 @@ (asserts! (not (is-eq new-owner tx-sender)) ERR-ZERO-ADDRESS) (var-set contract-owner new-owner) (ok true) - ) -) + )) From 6eedb384f64e254825e7c4f5710a54b5fd58a176 Mon Sep 17 00:00:00 2001 From: Adenike Akande <38114140-1adenikeakande@users.noreply.replit.com> Date: Wed, 1 Jan 2025 04:04:36 +0000 Subject: [PATCH 3/5] revamped the contract with several key security improvements --- contracts/RightsRegistry.clar | 244 +++++++++++++++------------------- 1 file changed, 107 insertions(+), 137 deletions(-) diff --git a/contracts/RightsRegistry.clar b/contracts/RightsRegistry.clar index 5f4f164..8f5d0bb 100644 --- a/contracts/RightsRegistry.clar +++ b/contracts/RightsRegistry.clar @@ -11,7 +11,7 @@ ) ) -;; Constants for validation +;; Error Constants (define-constant ERR-NOT-AUTHORIZED (err u100)) (define-constant ERR-INVALID-SONG (err u101)) (define-constant ERR-ALREADY-EXISTS (err u102)) @@ -23,6 +23,14 @@ (define-constant ERR-ZERO-ADDRESS (err u108)) (define-constant ERR-TOTAL-SHARE-EXCEEDED (err u109)) +;; Constants for validation +(define-constant MIN-SONG-ID u1) +(define-constant MAX-SONG-ID u1000000) +(define-constant MIN-TITLE-LENGTH u1) +(define-constant MAX-TITLE-LENGTH u256) +(define-constant VALID-ROLES (list "writer" "producer" "performer")) +(define-constant VALID-STATUSES (list "active" "inactive")) + ;; Data Variables (define-data-var contract-owner principal tx-sender) @@ -50,172 +58,134 @@ { total-share: uint } ) -;; Private Functions -(define-private (sanitize-song-id (id uint)) - (if (> id u0) - id - u0)) - -(define-private (sanitize-title (input (string-ascii 256))) - (if (is-eq input "") - "untitled" - input)) - -(define-private (sanitize-status (input (string-ascii 10))) - (if (or (is-eq input "active") (is-eq input "inactive")) - input - "inactive")) - -(define-private (sanitize-role (input (string-ascii 20))) - (if (or - (is-eq input "writer") - (is-eq input "producer") - (is-eq input "performer")) - input - "other")) - -(define-private (validate-song-id (id uint)) - (if (> id u0) - (ok id) - ERR-INVALID-SONG-ID)) - -(define-private (validate-title (input (string-ascii 256))) - (if (not (is-eq input "")) - (ok input) - ERR-INVALID-TITLE)) - -(define-private (validate-status (input (string-ascii 10))) - (if (or (is-eq input "active") (is-eq input "inactive")) - (ok input) - ERR-INVALID-STATUS)) - -(define-private (validate-role (input (string-ascii 20))) - (if (or - (is-eq input "writer") - (is-eq input "producer") - (is-eq input "performer")) - (ok input) +;; Private Helper Functions +(define-private (is-valid-song-id (id uint)) + (and + (>= id MIN-SONG-ID) + (<= id MAX-SONG-ID))) + +(define-private (is-valid-title (title (string-ascii 256))) + (and + (>= (len title) MIN-TITLE-LENGTH) + (<= (len title) MAX-TITLE-LENGTH))) + +(define-private (is-valid-role (role (string-ascii 20))) + (asserts! + (or + (is-eq role "writer") + (is-eq role "producer") + (is-eq role "performer")) ERR-INVALID-ROLE)) -(define-private (get-total-share (song-id uint)) - (get total-share - (default-to - { total-share: u0 } - (map-get? total-song-shares { song-id: (sanitize-song-id song-id) })))) +(define-private (is-valid-status (status (string-ascii 10))) + (asserts! + (or + (is-eq status "active") + (is-eq status "inactive")) + ERR-INVALID-STATUS)) + +(define-private (check-authorization) + (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) ;; Read-Only Functions (define-read-only (get-song-details (song-id uint)) - (let - ((safe-id (sanitize-song-id song-id))) - (ok (map-get? rights-registry { song-id: safe-id })))) + (begin + (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (ok (map-get? rights-registry { song-id: song-id })))) (define-read-only (get-collaborator-share (song-id uint) (collaborator principal)) - (let - ((safe-id (sanitize-song-id song-id))) - (ok (map-get? royalty-splits - { song-id: safe-id, collaborator: collaborator })))) + (begin + (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })))) ;; Public Functions (define-public (register-song (song-id uint) (title (string-ascii 256))) - (let - ((safe-id (sanitize-song-id song-id)) - (safe-title (sanitize-title title))) - - ;; Input validation - (try! (validate-song-id safe-id)) - (try! (validate-title safe-title)) - - ;; Authorization check - (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) - - ;; Check if song already exists - (asserts! (is-none (map-get? rights-registry { song-id: safe-id })) ERR-ALREADY-EXISTS) - - ;; Register the song + (begin + ;; Validate inputs first + (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (asserts! (is-valid-title title) ERR-INVALID-TITLE) + + ;; Check authorization + (check-authorization) + + ;; Verify song doesn't exist + (asserts! (is-none (map-get? rights-registry { song-id: song-id })) ERR-ALREADY-EXISTS) + + ;; Register song (map-set rights-registry - { song-id: safe-id } + { song-id: song-id } { owner: tx-sender, - title: safe-title, + title: title, created-at: block-height, status: "active" - } - ) - - ;; Initialize total share + }) + + ;; Initialize shares (map-set total-song-shares - { song-id: safe-id } - { total-share: u0 } - ) - - (ok true) - )) + { song-id: song-id } + { total-share: u0 }) + + (ok true))) (define-public (add-collaborator (song-id uint) (collaborator principal) (share uint) (role (string-ascii 20))) - (let - ((safe-id (sanitize-song-id song-id)) - (safe-role (sanitize-role role))) - - ;; Input validation - (try! (validate-song-id safe-id)) - (try! (validate-role safe-role)) + (begin + ;; Validate all inputs first + (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (try! (is-valid-role role)) (asserts! (not (is-eq collaborator tx-sender)) ERR-ZERO-ADDRESS) (asserts! (<= share u10000) ERR-INVALID-SHARE) - - ;; Get song details and validate - (let ((song-exists (map-get? rights-registry { song-id: safe-id })) - (current-total (get-total-share safe-id))) - - (asserts! (is-some song-exists) ERR-INVALID-SONG) - (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - - ;; Check if total share would exceed 100% - (asserts! (<= (+ share current-total) u10000) ERR-TOTAL-SHARE-EXCEEDED) - - ;; Update collaborator share + + (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG)) + (current-shares (default-to { total-share: u0 } + (map-get? total-song-shares { song-id: song-id })))) + + ;; Verify ownership + (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) + + ;; Check total shares + (asserts! (<= (+ share (get total-share current-shares)) u10000) + ERR-TOTAL-SHARE-EXCEEDED) + + ;; Update collaborator (map-set royalty-splits - { song-id: safe-id, collaborator: collaborator } - { share: share, role: safe-role } - ) - - ;; Update total share + { song-id: song-id, collaborator: collaborator } + { share: share, role: role }) + + ;; Update total shares (map-set total-song-shares - { song-id: safe-id } - { total-share: (+ share current-total) } - ) - - (ok true) - ))) - -(define-public (update-song-status (song-id uint) (new-status (string-ascii 10))) - (let - ((safe-id (sanitize-song-id song-id)) - (safe-status (sanitize-status new-status))) - - ;; Input validation - (try! (validate-song-id safe-id)) - (try! (validate-status safe-status)) - - (let ((song-exists (map-get? rights-registry { song-id: safe-id }))) - (asserts! (is-some song-exists) ERR-INVALID-SONG) - (asserts! (is-eq tx-sender (get owner (unwrap-panic song-exists))) ERR-NOT-AUTHORIZED) - + { song-id: song-id } + { total-share: (+ share (get total-share current-shares)) }) + + (ok true)))) + +(define-public (update-song-status + (song-id uint) + (new-status (string-ascii 10))) + (begin + ;; Validate inputs first + (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (try! (is-valid-status new-status)) + + (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG))) + ;; Verify ownership + (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) + + ;; Update status (map-set rights-registry - { song-id: safe-id } - (merge (unwrap-panic song-exists) { status: safe-status }) - ) - (ok true) - ))) + { song-id: song-id } + (merge song-details { status: new-status })) + + (ok true)))) ;; Administrative Functions (define-public (transfer-ownership (new-owner principal)) (begin - (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) + (check-authorization) (asserts! (not (is-eq new-owner tx-sender)) ERR-ZERO-ADDRESS) (var-set contract-owner new-owner) - (ok true) - )) + (ok true))) From 6bb20ac601e2a6847f3e00e9687a8f9ce242c612 Mon Sep 17 00:00:00 2001 From: Adenike Akande <38114140-1adenikeakande@users.noreply.replit.com> Date: Wed, 1 Jan 2025 04:05:30 +0000 Subject: [PATCH 4/5] Fixed validation functions to consistently return --- contracts/RightsRegistry.clar | 96 ++++++++++++++++------------------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/contracts/RightsRegistry.clar b/contracts/RightsRegistry.clar index 8f5d0bb..fbba5f2 100644 --- a/contracts/RightsRegistry.clar +++ b/contracts/RightsRegistry.clar @@ -23,14 +23,6 @@ (define-constant ERR-ZERO-ADDRESS (err u108)) (define-constant ERR-TOTAL-SHARE-EXCEEDED (err u109)) -;; Constants for validation -(define-constant MIN-SONG-ID u1) -(define-constant MAX-SONG-ID u1000000) -(define-constant MIN-TITLE-LENGTH u1) -(define-constant MAX-TITLE-LENGTH u256) -(define-constant VALID-ROLES (list "writer" "producer" "performer")) -(define-constant VALID-STATUSES (list "active" "inactive")) - ;; Data Variables (define-data-var contract-owner principal tx-sender) @@ -60,57 +52,57 @@ ;; Private Helper Functions (define-private (is-valid-song-id (id uint)) - (and - (>= id MIN-SONG-ID) - (<= id MAX-SONG-ID))) + (if (and (> id u0) (< id u1000000)) + (ok true) + ERR-INVALID-SONG-ID)) (define-private (is-valid-title (title (string-ascii 256))) - (and - (>= (len title) MIN-TITLE-LENGTH) - (<= (len title) MAX-TITLE-LENGTH))) + (if (and (> (len title) u0) (<= (len title) u256)) + (ok true) + ERR-INVALID-TITLE)) (define-private (is-valid-role (role (string-ascii 20))) - (asserts! - (or - (is-eq role "writer") - (is-eq role "producer") - (is-eq role "performer")) + (if (or + (is-eq role "writer") + (is-eq role "producer") + (is-eq role "performer")) + (ok true) ERR-INVALID-ROLE)) (define-private (is-valid-status (status (string-ascii 10))) - (asserts! - (or - (is-eq status "active") - (is-eq status "inactive")) + (if (or + (is-eq status "active") + (is-eq status "inactive")) + (ok true) ERR-INVALID-STATUS)) (define-private (check-authorization) - (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)) + (if (is-eq tx-sender (var-get contract-owner)) + (ok true) + ERR-NOT-AUTHORIZED)) ;; Read-Only Functions (define-read-only (get-song-details (song-id uint)) (begin - (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (try! (is-valid-song-id song-id)) (ok (map-get? rights-registry { song-id: song-id })))) (define-read-only (get-collaborator-share (song-id uint) (collaborator principal)) (begin - (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + (try! (is-valid-song-id song-id)) (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })))) ;; Public Functions (define-public (register-song (song-id uint) (title (string-ascii 256))) (begin - ;; Validate inputs first - (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) - (asserts! (is-valid-title title) ERR-INVALID-TITLE) - - ;; Check authorization - (check-authorization) - - ;; Verify song doesn't exist + ;; Input validation + (try! (is-valid-song-id song-id)) + (try! (is-valid-title title)) + (try! (check-authorization)) + + ;; Check if song exists (asserts! (is-none (map-get? rights-registry { song-id: song-id })) ERR-ALREADY-EXISTS) - + ;; Register song (map-set rights-registry { song-id: song-id } @@ -120,12 +112,12 @@ created-at: block-height, status: "active" }) - + ;; Initialize shares (map-set total-song-shares { song-id: song-id } { total-share: u0 }) - + (ok true))) (define-public (add-collaborator @@ -134,58 +126,60 @@ (share uint) (role (string-ascii 20))) (begin - ;; Validate all inputs first - (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + ;; Input validation + (try! (is-valid-song-id song-id)) (try! (is-valid-role role)) + + ;; Additional validations (asserts! (not (is-eq collaborator tx-sender)) ERR-ZERO-ADDRESS) (asserts! (<= share u10000) ERR-INVALID-SHARE) - + (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG)) (current-shares (default-to { total-share: u0 } (map-get? total-song-shares { song-id: song-id })))) - + ;; Verify ownership (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) - + ;; Check total shares (asserts! (<= (+ share (get total-share current-shares)) u10000) ERR-TOTAL-SHARE-EXCEEDED) - + ;; Update collaborator (map-set royalty-splits { song-id: song-id, collaborator: collaborator } { share: share, role: role }) - + ;; Update total shares (map-set total-song-shares { song-id: song-id } { total-share: (+ share (get total-share current-shares)) }) - + (ok true)))) (define-public (update-song-status (song-id uint) (new-status (string-ascii 10))) (begin - ;; Validate inputs first - (asserts! (is-valid-song-id song-id) ERR-INVALID-SONG-ID) + ;; Input validation + (try! (is-valid-song-id song-id)) (try! (is-valid-status new-status)) - + (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG))) ;; Verify ownership (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) - + ;; Update status (map-set rights-registry { song-id: song-id } (merge song-details { status: new-status })) - + (ok true)))) ;; Administrative Functions (define-public (transfer-ownership (new-owner principal)) (begin - (check-authorization) + (try! (check-authorization)) (asserts! (not (is-eq new-owner tx-sender)) ERR-ZERO-ADDRESS) (var-set contract-owner new-owner) (ok true))) From f8dafcc703328b1d6f8a6ef385818d184de45940 Mon Sep 17 00:00:00 2001 From: Adenike Akande <38114140-1adenikeakande@users.noreply.replit.com> Date: Wed, 1 Jan 2025 04:06:14 +0000 Subject: [PATCH 5/5] restored the working version and made targeted fixes for proper error handling --- contracts/RightsRegistry.clar | 160 +++++++++++++++++----------------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/contracts/RightsRegistry.clar b/contracts/RightsRegistry.clar index fbba5f2..d249606 100644 --- a/contracts/RightsRegistry.clar +++ b/contracts/RightsRegistry.clar @@ -1,3 +1,6 @@ +;; Rights Registry Contract +;; Handles music rights ownership, royalty splits, and rights transfers + ;; Define SIP-010 trait (define-trait sip-010-trait ( @@ -11,7 +14,7 @@ ) ) -;; Error Constants +;; Constants (define-constant ERR-NOT-AUTHORIZED (err u100)) (define-constant ERR-INVALID-SONG (err u101)) (define-constant ERR-ALREADY-EXISTS (err u102)) @@ -33,15 +36,15 @@ owner: principal, title: (string-ascii 256), created-at: uint, - status: (string-ascii 10) + status: (string-ascii 10) ;; "active" or "inactive" } ) (define-map royalty-splits { song-id: uint, collaborator: principal } { - share: uint, - role: (string-ascii 20) + share: uint, ;; Percentage * 100 (e.g., 2500 = 25%) + role: (string-ascii 20) ;; e.g., "writer", "producer", "performer" } ) @@ -50,55 +53,30 @@ { total-share: uint } ) -;; Private Helper Functions -(define-private (is-valid-song-id (id uint)) - (if (and (> id u0) (< id u1000000)) - (ok true) - ERR-INVALID-SONG-ID)) - -(define-private (is-valid-title (title (string-ascii 256))) - (if (and (> (len title) u0) (<= (len title) u256)) - (ok true) - ERR-INVALID-TITLE)) - -(define-private (is-valid-role (role (string-ascii 20))) - (if (or - (is-eq role "writer") - (is-eq role "producer") - (is-eq role "performer")) - (ok true) - ERR-INVALID-ROLE)) - -(define-private (is-valid-status (status (string-ascii 10))) - (if (or - (is-eq status "active") - (is-eq status "inactive")) - (ok true) - ERR-INVALID-STATUS)) - -(define-private (check-authorization) - (if (is-eq tx-sender (var-get contract-owner)) - (ok true) - ERR-NOT-AUTHORIZED)) - ;; Read-Only Functions (define-read-only (get-song-details (song-id uint)) (begin - (try! (is-valid-song-id song-id)) - (ok (map-get? rights-registry { song-id: song-id })))) + (asserts! (> song-id u0) ERR-INVALID-SONG-ID) + (ok (map-get? rights-registry { song-id: song-id })) + ) +) (define-read-only (get-collaborator-share (song-id uint) (collaborator principal)) (begin - (try! (is-valid-song-id song-id)) - (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })))) + (asserts! (> song-id u0) ERR-INVALID-SONG-ID) + (ok (map-get? royalty-splits { song-id: song-id, collaborator: collaborator })) + ) +) ;; Public Functions (define-public (register-song (song-id uint) (title (string-ascii 256))) (begin ;; Input validation - (try! (is-valid-song-id song-id)) - (try! (is-valid-title title)) - (try! (check-authorization)) + (asserts! (> song-id u0) ERR-INVALID-SONG-ID) + (asserts! (not (is-eq title "")) ERR-INVALID-TITLE) + + ;; Authorization check + (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) ;; Check if song exists (asserts! (is-none (map-get? rights-registry { song-id: song-id })) ERR-ALREADY-EXISTS) @@ -111,14 +89,18 @@ title: title, created-at: block-height, status: "active" - }) + } + ) - ;; Initialize shares + ;; Initialize total share (map-set total-song-shares { song-id: song-id } - { total-share: u0 }) + { total-share: u0 } + ) - (ok true))) + (ok true) + ) +) (define-public (add-collaborator (song-id uint) @@ -127,59 +109,79 @@ (role (string-ascii 20))) (begin ;; Input validation - (try! (is-valid-song-id song-id)) - (try! (is-valid-role role)) - - ;; Additional validations + (asserts! (> song-id u0) ERR-INVALID-SONG-ID) (asserts! (not (is-eq collaborator tx-sender)) ERR-ZERO-ADDRESS) (asserts! (<= share u10000) ERR-INVALID-SHARE) + (asserts! + (or + (is-eq role "writer") + (is-eq role "producer") + (is-eq role "performer") + ) + ERR-INVALID-ROLE + ) - (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG)) - (current-shares (default-to { total-share: u0 } - (map-get? total-song-shares { song-id: song-id })))) + ;; Get song details and validate + (let ( + (song-exists (unwrap! (get-song-details song-id) ERR-INVALID-SONG)) + (current-shares (default-to { total-share: u0 } + (map-get? total-song-shares { song-id: song-id }))) + ) - ;; Verify ownership - (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) + (asserts! (is-some song-exists) ERR-INVALID-SONG) + (asserts! (is-eq tx-sender (get owner (unwrap! song-exists ERR-INVALID-SONG))) ERR-NOT-AUTHORIZED) - ;; Check total shares - (asserts! (<= (+ share (get total-share current-shares)) u10000) - ERR-TOTAL-SHARE-EXCEEDED) + ;; Check if total share would exceed 100% + (asserts! (<= (+ share (get total-share current-shares)) u10000) ERR-TOTAL-SHARE-EXCEEDED) - ;; Update collaborator + ;; Update collaborator share (map-set royalty-splits { song-id: song-id, collaborator: collaborator } - { share: share, role: role }) - - ;; Update total shares + { share: share, role: role } + ) + + ;; Update total share (map-set total-song-shares { song-id: song-id } - { total-share: (+ share (get total-share current-shares)) }) - - (ok true)))) + { total-share: (+ share (get total-share current-shares)) } + ) + + (ok true) + ) + ) +) -(define-public (update-song-status - (song-id uint) - (new-status (string-ascii 10))) +(define-public (update-song-status (song-id uint) (new-status (string-ascii 10))) (begin ;; Input validation - (try! (is-valid-song-id song-id)) - (try! (is-valid-status new-status)) + (asserts! (> song-id u0) ERR-INVALID-SONG-ID) + (asserts! + (or + (is-eq new-status "active") + (is-eq new-status "inactive") + ) + ERR-INVALID-STATUS + ) - (let ((song-details (unwrap! (get-song-details song-id) ERR-INVALID-SONG))) - ;; Verify ownership - (asserts! (is-eq tx-sender (get owner song-details)) ERR-NOT-AUTHORIZED) + (let ((song-exists (unwrap! (get-song-details song-id) ERR-INVALID-SONG))) + (asserts! (is-some song-exists) ERR-INVALID-SONG) + (asserts! (is-eq tx-sender (get owner (unwrap! song-exists ERR-INVALID-SONG))) ERR-NOT-AUTHORIZED) - ;; Update status (map-set rights-registry { song-id: song-id } - (merge song-details { status: new-status })) - - (ok true)))) + (merge (unwrap! song-exists ERR-INVALID-SONG) { status: new-status }) + ) + (ok true) + ) + ) +) ;; Administrative Functions (define-public (transfer-ownership (new-owner principal)) (begin - (try! (check-authorization)) + (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED) (asserts! (not (is-eq new-owner tx-sender)) ERR-ZERO-ADDRESS) (var-set contract-owner new-owner) - (ok true))) + (ok true) + ) +)