From 31265b1cadd3d2dfda0f3bc73ccc5807f8a80a9d Mon Sep 17 00:00:00 2001 From: bukas898 Date: Sat, 30 Nov 2024 22:21:32 +0100 Subject: [PATCH 1/5] feat: add basic artwork registration with creator authorization --- meta_dir/.gitattributes | 3 + meta_dir/.gitignore | 13 +++ meta_dir/.vscode/settings.json | 4 + meta_dir/.vscode/tasks.json | 19 ++++ meta_dir/Clarinet.toml | 19 ++++ meta_dir/contracts/codehub.clar | 38 ++++++++ meta_dir/package.json | 24 +++++ meta_dir/settings/Devnet.toml | 157 ++++++++++++++++++++++++++++++++ meta_dir/tests/codehub.test.ts | 21 +++++ meta_dir/tsconfig.json | 26 ++++++ meta_dir/vitest.config.js | 42 +++++++++ 11 files changed, 366 insertions(+) create mode 100644 meta_dir/.gitattributes create mode 100644 meta_dir/.gitignore create mode 100644 meta_dir/.vscode/settings.json create mode 100644 meta_dir/.vscode/tasks.json create mode 100644 meta_dir/Clarinet.toml create mode 100644 meta_dir/contracts/codehub.clar create mode 100644 meta_dir/package.json create mode 100644 meta_dir/settings/Devnet.toml create mode 100644 meta_dir/tests/codehub.test.ts create mode 100644 meta_dir/tsconfig.json create mode 100644 meta_dir/vitest.config.js diff --git a/meta_dir/.gitattributes b/meta_dir/.gitattributes new file mode 100644 index 0000000..da6a065 --- /dev/null +++ b/meta_dir/.gitattributes @@ -0,0 +1,3 @@ +tests/** linguist-vendored +vitest.config.js linguist-vendored +* text=lf diff --git a/meta_dir/.gitignore b/meta_dir/.gitignore new file mode 100644 index 0000000..76c2842 --- /dev/null +++ b/meta_dir/.gitignore @@ -0,0 +1,13 @@ + +**/settings/Mainnet.toml +**/settings/Testnet.toml +.cache/** +history.txt + +logs +*.log +npm-debug.log* +coverage +*.info +costs-reports.json +node_modules diff --git a/meta_dir/.vscode/settings.json b/meta_dir/.vscode/settings.json new file mode 100644 index 0000000..3062519 --- /dev/null +++ b/meta_dir/.vscode/settings.json @@ -0,0 +1,4 @@ + +{ + "files.eol": "\n" +} diff --git a/meta_dir/.vscode/tasks.json b/meta_dir/.vscode/tasks.json new file mode 100644 index 0000000..4dec0ff --- /dev/null +++ b/meta_dir/.vscode/tasks.json @@ -0,0 +1,19 @@ + +{ + "version": "2.0.0", + "tasks": [ + { + "label": "check contracts", + "group": "test", + "type": "shell", + "command": "clarinet check" + }, + { + "type": "npm", + "script": "test", + "group": "test", + "problemMatcher": [], + "label": "npm test" + } + ] +} diff --git a/meta_dir/Clarinet.toml b/meta_dir/Clarinet.toml new file mode 100644 index 0000000..dc2814b --- /dev/null +++ b/meta_dir/Clarinet.toml @@ -0,0 +1,19 @@ +[project] +name = 'meta_dir' +description = '' +authors = [] +telemetry = true +cache_dir = '.\.cache' +requirements = [] +[contracts.codehub] +path = 'contracts/codehub.clar' +clarity_version = 3 +epoch = 3.0 +[repl.analysis] +passes = ['check_checker'] + +[repl.analysis.check_checker] +strict = false +trusted_sender = false +trusted_caller = false +callee_filter = false diff --git a/meta_dir/contracts/codehub.clar b/meta_dir/contracts/codehub.clar new file mode 100644 index 0000000..2531543 --- /dev/null +++ b/meta_dir/contracts/codehub.clar @@ -0,0 +1,38 @@ +;; METACANVAS: Basic Artwork Registration +;; Author: Amobi Ndubuisi + +;; Data map to store artwork details +(define-map artworks + { token-id: (buff 32) } + { artist: principal, collection: (buff 64), timestamp: uint }) + +;; Authorized creators +(define-set creators (principal)) + +;; Constants for errors +(define-constant ERR_NOT_AUTHORIZED u100) +(define-constant ERR_ALREADY_EXISTS u103) + +;; Function to register a creator +(define-public (register-creator (creator principal)) + (begin + (set (creators creator)) + (ok creator) + ) +) + +;; Function to mint artwork +(define-public (mint-artwork (token-id (buff 32)) (artist principal) (collection (buff 64))) + (if (not (contains? creators tx-sender)) + (err ERR_NOT_AUTHORIZED) + (if (map-get artworks { token-id: token-id }) + (err ERR_ALREADY_EXISTS) + (begin + (map-set artworks + { token-id: token-id } + { artist: artist, collection: collection, timestamp: (block-height) }) + (ok token-id) + ) + ) + ) +) diff --git a/meta_dir/package.json b/meta_dir/package.json new file mode 100644 index 0000000..a3a9e8a --- /dev/null +++ b/meta_dir/package.json @@ -0,0 +1,24 @@ + +{ + "name": "meta_dir-tests", + "version": "1.0.0", + "description": "Run unit tests on this project.", + "type": "module", + "private": true, + "scripts": { + "test": "vitest run", + "test:report": "vitest run -- --coverage --costs", + "test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\"" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@hirosystems/clarinet-sdk": "^2.3.2", + "@stacks/transactions": "^6.12.0", + "chokidar-cli": "^3.0.0", + "typescript": "^5.3.3", + "vite": "^5.1.4", + "vitest": "^1.3.1", + "vitest-environment-clarinet": "^2.0.0" + } +} diff --git a/meta_dir/settings/Devnet.toml b/meta_dir/settings/Devnet.toml new file mode 100644 index 0000000..54a2c35 --- /dev/null +++ b/meta_dir/settings/Devnet.toml @@ -0,0 +1,157 @@ +[network] +name = "devnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +balance = 100_000_000_000_000 +# secret_key: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601 +# stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +# btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH + +[accounts.wallet_1] +mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild" +balance = 100_000_000_000_000 +# secret_key: 7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801 +# stx_address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +# btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC + +[accounts.wallet_2] +mnemonic = "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital" +balance = 100_000_000_000_000 +# secret_key: 530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101 +# stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG +# btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + +[accounts.wallet_3] +mnemonic = "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high" +balance = 100_000_000_000_000 +# secret_key: d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901 +# stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC +# btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + +[accounts.wallet_4] +mnemonic = "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin" +balance = 100_000_000_000_000 +# secret_key: f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701 +# stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND +# btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8 + +[accounts.wallet_5] +mnemonic = "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase" +balance = 100_000_000_000_000 +# secret_key: 3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801 +# stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB +# btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx + +[accounts.wallet_6] +mnemonic = "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy" +balance = 100_000_000_000_000 +# secret_key: 7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01 +# stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 +# btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt + +[accounts.wallet_7] +mnemonic = "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow" +balance = 100_000_000_000_000 +# secret_key: b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401 +# stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ +# btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7 + +[accounts.wallet_8] +mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune" +balance = 100_000_000_000_000 +# secret_key: 6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01 +# stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP +# btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw + +[accounts.faucet] +mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" +balance = 100_000_000_000_000 +# secret_key: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801 +# stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 +# btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + +[devnet] +disable_stacks_explorer = false +disable_stacks_api = false +# disable_postgres = false +# disable_subnet_api = false +# disable_bitcoin_explorer = true +# working_dir = "tmp/devnet" +# stacks_node_events_observers = ["host.docker.internal:8002"] +# miner_mnemonic = "fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce" +# miner_derivation_path = "m/44'/5757'/0'/0/0" +# faucet_mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" +# faucet_derivation_path = "m/44'/5757'/0'/0/0" +# stacker_mnemonic = "empty lens any direct brother then drop fury rule pole win claim scissors list rescue horn rent inform relief jump sword weekend half legend" +# stacker_derivation_path = "m/44'/5757'/0'/0/0" +# orchestrator_port = 20445 +# bitcoin_node_p2p_port = 18444 +# bitcoin_node_rpc_port = 18443 +# bitcoin_node_username = "devnet" +# bitcoin_node_password = "devnet" +# bitcoin_controller_block_time = 30_000 +# stacks_node_rpc_port = 20443 +# stacks_node_p2p_port = 20444 +# stacks_api_port = 3999 +# stacks_api_events_port = 3700 +# bitcoin_explorer_port = 8001 +# stacks_explorer_port = 8000 +# postgres_port = 5432 +# postgres_username = "postgres" +# postgres_password = "postgres" +# postgres_database = "postgres" +# bitcoin_node_image_url = "quay.io/hirosystems/bitcoind:26.0" +# stacks_node_image_url = "quay.io/hirosystems/stacks-node:devnet-3.0" +# stacks_signer_image_url = "quay.io/hirosystems/stacks-signer:devnet-3.0" +# stacks_api_image_url = "hirosystems/stacks-blockchain-api:master" +# stacks_explorer_image_url = "hirosystems/explorer:latest" +# bitcoin_explorer_image_url = "quay.io/hirosystems/bitcoin-explorer:devnet" +# postgres_image_url = "postgres:alpine" +# enable_subnet_node = true +# subnet_node_image_url = "hirosystems/stacks-subnets:0.8.1" +# subnet_leader_mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +# subnet_leader_derivation_path = "m/44'/5757'/0'/0/0" +# subnet_contract_id = "ST173JK7NZBA4BS05ZRATQH1K89YJMTGEH1Z5J52E.subnet-v3-0-1" +# subnet_node_rpc_port = 30443 +# subnet_node_p2p_port = 30444 +# subnet_events_ingestion_port = 30445 +# subnet_node_events_observers = ["host.docker.internal:8002"] +# subnet_api_image_url = "hirosystems/stacks-blockchain-api:master" +# subnet_api_postgres_database = "subnet_api" + +# epoch_2_0 = 100 +# epoch_2_05 = 100 +# epoch_2_1 = 101 +# epoch_2_2 = 102 +# epoch_2_3 = 103 +# epoch_2_4 = 104 +# epoch_2_5 = 108 +# epoch_3_0 = 142 + +# Send some stacking orders +[[devnet.pox_stacking_orders]] +start_at_cycle = 1 +duration = 10 +auto_extend = true +wallet = "wallet_1" +slots = 2 +btc_address = "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" + +[[devnet.pox_stacking_orders]] +start_at_cycle = 1 +duration = 10 +auto_extend = true +wallet = "wallet_2" +slots = 2 +btc_address = "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG" + +[[devnet.pox_stacking_orders]] +start_at_cycle = 1 +duration = 10 +auto_extend = true +wallet = "wallet_3" +slots = 2 +btc_address = "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7" + diff --git a/meta_dir/tests/codehub.test.ts b/meta_dir/tests/codehub.test.ts new file mode 100644 index 0000000..4bb9cf3 --- /dev/null +++ b/meta_dir/tests/codehub.test.ts @@ -0,0 +1,21 @@ + +import { describe, expect, it } from "vitest"; + +const accounts = simnet.getAccounts(); +const address1 = accounts.get("wallet_1")!; + +/* + The test below is an example. To learn more, read the testing documentation here: + https://docs.hiro.so/stacks/clarinet-js-sdk +*/ + +describe("example tests", () => { + it("ensures simnet is well initalised", () => { + expect(simnet.blockHeight).toBeDefined(); + }); + + // it("shows an example", () => { + // const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1); + // expect(result).toBeUint(0); + // }); +}); diff --git a/meta_dir/tsconfig.json b/meta_dir/tsconfig.json new file mode 100644 index 0000000..1bdaf36 --- /dev/null +++ b/meta_dir/tsconfig.json @@ -0,0 +1,26 @@ + +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", + "tests" + ] +} diff --git a/meta_dir/vitest.config.js b/meta_dir/vitest.config.js new file mode 100644 index 0000000..c6a8506 --- /dev/null +++ b/meta_dir/vitest.config.js @@ -0,0 +1,42 @@ + +/// + +import { defineConfig } from "vite"; +import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest"; + +/* + In this file, Vitest is configured so that it works seamlessly with Clarinet and the Simnet. + + The `vitest-environment-clarinet` will initialise the clarinet-sdk + and make the `simnet` object available globally in the test files. + + `vitestSetupFilePath` points to a file in the `@hirosystems/clarinet-sdk` package that does two things: + - run `before` hooks to initialize the simnet and `after` hooks to collect costs and coverage reports. + - load custom vitest matchers to work with Clarity values (such as `expect(...).toBeUint()`) + + The `getClarinetVitestsArgv()` will parse options passed to the command `vitest run --` + - vitest run -- --manifest ./Clarinet.toml # pass a custom path + - vitest run -- --coverage --costs # collect coverage and cost reports +*/ + +export default defineConfig({ + test: { + environment: "clarinet", // use vitest-environment-clarinet + pool: "forks", + poolOptions: { + threads: { singleThread: true }, + forks: { singleFork: true }, + }, + setupFiles: [ + vitestSetupFilePath, + // custom setup files can be added here + ], + environmentOptions: { + clarinet: { + ...getClarinetVitestsArgv(), + // add or override options + }, + }, + }, +}); + From b4e2ccc2b74c708484d3a80742dfd83141056745 Mon Sep 17 00:00:00 2001 From: bukas898 Date: Sat, 30 Nov 2024 22:24:57 +0100 Subject: [PATCH 2/5] feat: enable license transfer and ownership tracking --- meta_dir/contracts/codehub.clar | 38 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/meta_dir/contracts/codehub.clar b/meta_dir/contracts/codehub.clar index 2531543..22b7339 100644 --- a/meta_dir/contracts/codehub.clar +++ b/meta_dir/contracts/codehub.clar @@ -1,27 +1,28 @@ -;; METACANVAS: Basic Artwork Registration -;; Author: Amobi Ndubuisi +;; METACANVA: License Transfer -;; Data map to store artwork details -(define-map artworks +;; Extending Stage 1 +(define-map licenses { token-id: (buff 32) } - { artist: principal, collection: (buff 64), timestamp: uint }) + { owner: principal }) -;; Authorized creators -(define-set creators (principal)) - -;; Constants for errors -(define-constant ERR_NOT_AUTHORIZED u100) -(define-constant ERR_ALREADY_EXISTS u103) - -;; Function to register a creator -(define-public (register-creator (creator principal)) - (begin - (set (creators creator)) - (ok creator) +;; Function to transfer license +(define-public (transfer-license (token-id (buff 32)) (new-owner principal)) + (let + ((license (map-get licenses { token-id: token-id }))) + (if (is-none license) + (err u102) ;; ERR_NOT_FOUND + (if (is-eq (get owner (unwrap license u102)) tx-sender) + (begin + (map-set licenses { token-id: token-id } { owner: new-owner }) + (ok token-id) + ) + (err ERR_NOT_AUTHORIZED) + ) + ) ) ) -;; Function to mint artwork +;; Update mint-artwork to initialize license owner (define-public (mint-artwork (token-id (buff 32)) (artist principal) (collection (buff 64))) (if (not (contains? creators tx-sender)) (err ERR_NOT_AUTHORIZED) @@ -31,6 +32,7 @@ (map-set artworks { token-id: token-id } { artist: artist, collection: collection, timestamp: (block-height) }) + (map-set licenses { token-id: token-id } { owner: artist }) (ok token-id) ) ) From 278a835d91ca089eb924d9c2a40face9eddc7485 Mon Sep 17 00:00:00 2001 From: bukas898 Date: Sat, 30 Nov 2024 22:34:27 +0100 Subject: [PATCH 3/5] feat: add license status management and artwork verification --- meta_dir/contracts/codehub.clar | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/meta_dir/contracts/codehub.clar b/meta_dir/contracts/codehub.clar index 22b7339..17922dc 100644 --- a/meta_dir/contracts/codehub.clar +++ b/meta_dir/contracts/codehub.clar @@ -1,19 +1,19 @@ -;; METACANVA: License Transfer +;; METACANVA: License Status Management and Verification -;; Extending Stage 1 -(define-map licenses +;; Extending Stage 2 +(define-map license-status { token-id: (buff 32) } - { owner: principal }) + { status: (buff 16) }) -;; Function to transfer license -(define-public (transfer-license (token-id (buff 32)) (new-owner principal)) +;; Function to update license status +(define-public (update-license-status (token-id (buff 32)) (status (buff 16))) (let ((license (map-get licenses { token-id: token-id }))) (if (is-none license) (err u102) ;; ERR_NOT_FOUND (if (is-eq (get owner (unwrap license u102)) tx-sender) (begin - (map-set licenses { token-id: token-id } { owner: new-owner }) + (map-set license-status { token-id: token-id } { status: status }) (ok token-id) ) (err ERR_NOT_AUTHORIZED) @@ -22,19 +22,10 @@ ) ) -;; Update mint-artwork to initialize license owner -(define-public (mint-artwork (token-id (buff 32)) (artist principal) (collection (buff 64))) - (if (not (contains? creators tx-sender)) - (err ERR_NOT_AUTHORIZED) - (if (map-get artworks { token-id: token-id }) - (err ERR_ALREADY_EXISTS) - (begin - (map-set artworks - { token-id: token-id } - { artist: artist, collection: collection, timestamp: (block-height) }) - (map-set licenses { token-id: token-id } { owner: artist }) - (ok token-id) - ) - ) +;; Function to verify artwork +(define-read-only (verify-artwork (token-id (buff 32))) + (match (map-get artworks { token-id: token-id }) + artwork (ok { artist: (get artist artwork), collection: (get collection artwork) }) + (err u102) ;; ERR_NOT_FOUND ) ) From 815165a4f8844c9e59e82d32d986feef715e49f4 Mon Sep 17 00:00:00 2001 From: bukas898 Date: Sat, 30 Nov 2024 22:36:34 +0100 Subject: [PATCH 4/5] expanded fuction scope and enhancd security --- meta_dir/contracts/codehub.clar | 258 ++++++++++++++++++++++++++++---- 1 file changed, 232 insertions(+), 26 deletions(-) diff --git a/meta_dir/contracts/codehub.clar b/meta_dir/contracts/codehub.clar index 17922dc..f2f37f1 100644 --- a/meta_dir/contracts/codehub.clar +++ b/meta_dir/contracts/codehub.clar @@ -1,31 +1,237 @@ -;; METACANVA: License Status Management and Verification - -;; Extending Stage 2 -(define-map license-status - { token-id: (buff 32) } - { status: (buff 16) }) - -;; Function to update license status -(define-public (update-license-status (token-id (buff 32)) (status (buff 16))) - (let - ((license (map-get licenses { token-id: token-id }))) - (if (is-none license) - (err u102) ;; ERR_NOT_FOUND - (if (is-eq (get owner (unwrap license u102)) tx-sender) - (begin - (map-set license-status { token-id: token-id } { status: status }) - (ok token-id) - ) - (err ERR_NOT_AUTHORIZED) +;; Digital Art Authentication and Licensing System +;; Description: Smart contract system for authenticating and managing digital artwork licenses on Stacks blockchain + +;; Constants +(define-constant CONTRACT_OWNER tx-sender) +(define-constant ERR_NOT_AUTHORIZED (err u100)) +(define-constant ERR_INVALID_INPUT (err u101)) +(define-constant ERR_NOT_FOUND (err u102)) +(define-constant ERR_ALREADY_EXISTS (err u103)) +(define-constant ERR_INVALID_STATUS (err u104)) + +;; Status constants with explicit type casting +(define-constant LICENSE_VALID "valid") +(define-constant LICENSE_REVOKED "revoked") +(define-constant LICENSE_EXPIRED "expired") + +;; Data Variables +(define-data-var license-counter uint u0) + +;; Data Maps +(define-map artworks + {token-id: (string-ascii 50)} + { + artist: (string-ascii 50), + collection: (string-ascii 50), + creator: principal, + timestamp: uint, + status: (string-ascii 20), + current-licensee: principal + } +) + +(define-map creators principal bool) +(define-map galleries principal bool) + +;; Artwork license history tracking +(define-map license-history + {token-id: (string-ascii 50), index: uint} + { + licensee: principal, + timestamp: uint, + action: (string-ascii 20) + } +) + +;; Track history indices +(define-map history-indices + (string-ascii 50) ;; token-id + uint ;; current index +) + +;; Read-only functions +(define-read-only (get-artwork (token-id (string-ascii 50))) + (map-get? artworks {token-id: token-id}) +) + +(define-read-only (is-creator (address principal)) + (default-to false (map-get? creators address)) +) + +(define-read-only (is-gallery (address principal)) + (default-to false (map-get? galleries address)) +) + +(define-read-only (get-current-timestamp) + (var-get license-counter) +) + +;; Administrative functions +(define-public (register-creator (creator principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED) + (asserts! (is-none (map-get? creators creator)) ERR_ALREADY_EXISTS) + (ok (map-set creators creator true)) + ) +) + +(define-public (register-gallery (gallery principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED) + (asserts! (is-none (map-get? galleries gallery)) ERR_ALREADY_EXISTS) + (ok (map-set galleries gallery true)) + ) +) + +;; Artwork registration +(define-public (mint-artwork + (token-id (string-ascii 50)) + (artist (string-ascii 50)) + (collection (string-ascii 50))) + + (let + ( + (creator tx-sender) + (timestamp (var-get license-counter)) + ) + + ;; Verify creator authorization + (asserts! (is-creator creator) ERR_NOT_AUTHORIZED) + + ;; Check artwork doesn't already exist + (asserts! (is-none (get-artwork token-id)) ERR_ALREADY_EXISTS) + + ;; Validate input + (asserts! (> (len artist) u0) ERR_INVALID_INPUT) + (asserts! (> (len collection) u0) ERR_INVALID_INPUT) + + ;; Register artwork + (map-set artworks + {token-id: token-id} + { + artist: artist, + collection: collection, + creator: creator, + timestamp: timestamp, + status: LICENSE_VALID, + current-licensee: creator + } + ) + + ;; Initialize history + (map-set license-history + {token-id: token-id, index: u0} + { + licensee: creator, + timestamp: timestamp, + action: "minted" + } + ) + (map-set history-indices token-id u1) + + ;; Increment license counter + (var-set license-counter (+ timestamp u1)) + + (ok true) + ) +) + +;; Transfer license +(define-public (transfer-license + (token-id (string-ascii 50)) + (new-licensee principal)) + + (let + ( + (artwork (unwrap! (get-artwork token-id) ERR_NOT_FOUND)) + (timestamp (var-get license-counter)) + (current-index (default-to u0 (map-get? history-indices token-id))) + ) + + ;; Verify sender owns the license + (asserts! (is-eq (get current-licensee artwork) tx-sender) ERR_NOT_AUTHORIZED) + + ;; Validate new licensee + (asserts! (or (is-creator new-licensee) (is-gallery new-licensee)) ERR_INVALID_INPUT) + + ;; Update artwork license + (map-set artworks + {token-id: token-id} + (merge artwork {current-licensee: new-licensee}) + ) + + ;; Record transfer in history + (map-set license-history + {token-id: token-id, index: current-index} + { + licensee: new-licensee, + timestamp: timestamp, + action: "transferred" + } + ) + (map-set history-indices token-id (+ current-index u1)) + + ;; Increment license counter + (var-set license-counter (+ timestamp u1)) + + (ok true) + ) +) + +;; Verify artwork authenticity +(define-public (verify-artwork + (token-id (string-ascii 50))) + + (let + ( + (artwork (unwrap! (get-artwork token-id) ERR_NOT_FOUND)) ) + + ;; Verify creator is still authorized + (asserts! (is-creator (get creator artwork)) ERR_NOT_AUTHORIZED) + + ;; Return verification result + (ok { + is-authentic: true, + creator: (get creator artwork), + current-licensee: (get current-licensee artwork), + status: (get status artwork) + }) ) - ) ) -;; Function to verify artwork -(define-read-only (verify-artwork (token-id (buff 32))) - (match (map-get artworks { token-id: token-id }) - artwork (ok { artist: (get artist artwork), collection: (get collection artwork) }) - (err u102) ;; ERR_NOT_FOUND - ) +;; Get license history +(define-read-only (get-license-history + (token-id (string-ascii 50)) + (index uint)) + + (map-get? license-history {token-id: token-id, index: index}) ) + +;; Update license status +(define-public (update-license-status + (token-id (string-ascii 50)) + (new-status (string-ascii 20))) + + (let + ( + (artwork (unwrap! (get-artwork token-id) ERR_NOT_FOUND)) + ) + + ;; Only creator can update status + (asserts! (is-eq (get creator artwork) tx-sender) ERR_NOT_AUTHORIZED) + + ;; Validate status + (asserts! (or + (is-eq new-status LICENSE_VALID) + (is-eq new-status LICENSE_REVOKED) + (is-eq new-status LICENSE_EXPIRED) + ) ERR_INVALID_STATUS) + + ;; Update status + (ok (map-set artworks + {token-id: token-id} + (merge artwork {status: new-status}) + )) + ) +) \ No newline at end of file From 6f6dc429a5da2f1e554cc1d738c8d55a0cddd274 Mon Sep 17 00:00:00 2001 From: bukas898 Date: Sat, 30 Nov 2024 23:07:03 +0100 Subject: [PATCH 5/5] updated README --- meta_dir/README.md | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 meta_dir/README.md diff --git a/meta_dir/README.md b/meta_dir/README.md new file mode 100644 index 0000000..cb48747 --- /dev/null +++ b/meta_dir/README.md @@ -0,0 +1,136 @@ +# Digital Art Authentication and Licensing System (DAALS) + +**Project Overview** +The Digital Art Authentication and Licensing System (DAALS) is a smart contract application built on the Stacks blockchain. It provides a secure and transparent platform for authenticating digital artwork, managing licensing, and tracking ownership history. By leveraging blockchain technology, DAALS ensures that artists, galleries, and collectors can trust the authenticity and licensing status of digital artworks. + +--- + +## Features +1. **Artwork Registration**: + - Artists can register their digital artwork with details such as the artist name, collection, and a unique token ID. + - Automatically tracks the creation timestamp and assigns the creator as the initial licensee. + +2. **License Transfer**: + - Enables transferring artwork licenses securely between users (e.g., from artist to gallery). + - Records all license transfers in a historical ledger for transparency. + +3. **Artwork Verification**: + - Provides a mechanism to verify the authenticity of digital artworks. + - Ensures only authorized creators and galleries are listed. + +4. **License Status Management**: + - Allows creators to update the license status of their artworks (e.g., valid, revoked, or expired). + +5. **License History Tracking**: + - Maintains a comprehensive history of all license-related actions, including transfers and updates. + +--- + +## Architecture +The system is implemented using Clarity smart contracts and comprises the following core components: + +- **Data Maps**: + - `artworks`: Stores metadata about each artwork. + - `license-history`: Tracks the history of licenses for each artwork. + - `creators` and `galleries`: Registers authorized creators and galleries. + +- **Constants**: + - Define reusable values like error codes and license statuses. + +- **Read-Only Functions**: + - `get-artwork`: Fetch artwork details. + - `is-creator` and `is-gallery`: Check authorization for creators and galleries. + - `get-license-history`: Retrieve license history records. + +- **Public Functions**: + - `register-creator` and `register-gallery`: Add authorized creators and galleries. + - `mint-artwork`: Register a new artwork. + - `transfer-license`: Transfer license ownership. + - `verify-artwork`: Validate authenticity. + - `update-license-status`: Modify the license status. + +--- + +## Installation + +### Prerequisites +- [Stacks Blockchain CLI](https://docs.hiro.so/get-started) +- Node.js (for running a local development server if needed) +- A text editor like VS Code + +### Steps +1. Clone the repository: + ```bash + git clone https://github.com/your-repo/DAALS.git + cd DAALS + ``` +2. Deploy the contract: + - Use the Stacks CLI to deploy the Clarity smart contract: + ```bash + clarity-cli deploy ./contracts/daals.clar + ``` + +3. Test the contract (optional): + - Write and run test cases in Clarity to validate the functionality. + +--- + +## Usage + +### Artwork Registration +1. Register yourself as a creator using the `register-creator` function. + Example: + ```clarity + (contract-call? .daals register-creator tx-sender) + ``` +2. Mint a new artwork: + ```clarity + (contract-call? .daals mint-artwork "unique-token-id" "artist-name" "collection-name") + ``` + +### Transferring Licenses +To transfer a license to a new owner: +```clarity +(contract-call? .daals transfer-license "unique-token-id" new-licensee-principal) +``` + +### Verifying Artworks +To verify the authenticity of an artwork: +```clarity +(contract-call? .daals verify-artwork "unique-token-id") +``` + +### Updating License Status +To update the license status (e.g., revoke or expire): +```clarity +(contract-call? .daals update-license-status "unique-token-id" "revoked") +``` + +--- + +## Error Codes +- `ERR_NOT_AUTHORIZED (u100)`: The caller does not have permission. +- `ERR_INVALID_INPUT (u101)`: Invalid input provided. +- `ERR_NOT_FOUND (u102)`: The requested artwork or record does not exist. +- `ERR_ALREADY_EXISTS (u103)`: The artwork or record already exists. +- `ERR_INVALID_STATUS (u104)`: Invalid license status provided. + +--- + +## Roadmap +1. **Phase 1**: Initial release with core features for registration, transfer, and verification. +2. **Phase 2**: Integration with NFT standards for tokenized artworks. +3. **Phase 3**: Build a web-based interface for creators and galleries. +4. **Phase 4**: Support for multi-chain compatibility. + +--- + +## Contributing +Contributions are welcome! Please fork the repository and submit a pull request with your changes. + +--- + + +## Contact +For any inquiries or suggestions, please contact **Amobi Ndubuisi** at dev.triggerfish@gmail.com. +