├── .coderabbit.yaml ├── .codespellrc ├── .editorconfig ├── .github ├── dependabot.yml ├── package.json └── workflows │ ├── add-to-project.yml │ ├── auto-merge.yml │ ├── build.yml │ ├── lint.yml │ ├── notify-draft.yml │ ├── notify-release.yml │ ├── publish.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .markdownlint.yaml ├── .releaserc.cjs ├── .size-limit.json ├── .sort-derives.toml ├── .taplo.toml ├── .yamllint.yaml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Cranky.toml ├── LICENSE ├── Makefile.toml ├── README.md ├── clippy.toml ├── contracts ├── axone-cognitarium │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ ├── examples │ │ ├── prolog-query │ │ │ ├── README.md │ │ │ └── query.pl │ │ └── sample-data.rdf.xml │ ├── src │ │ ├── bin │ │ │ └── schema.rs │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ ├── parser │ │ │ ├── ast.rs │ │ │ └── mod.rs │ │ ├── querier │ │ │ ├── engine.rs │ │ │ ├── expression.rs │ │ │ ├── mapper.rs │ │ │ ├── mod.rs │ │ │ ├── plan.rs │ │ │ ├── plan_builder.rs │ │ │ └── variable.rs │ │ ├── rdf │ │ │ ├── atom.rs │ │ │ ├── mapper.rs │ │ │ └── mod.rs │ │ ├── state │ │ │ ├── blank_nodes.rs │ │ │ ├── mod.rs │ │ │ ├── namespaces.rs │ │ │ ├── store.rs │ │ │ ├── test_util.rs │ │ │ └── triples.rs │ │ └── storer │ │ │ ├── engine.rs │ │ │ └── mod.rs │ ├── testdata │ │ ├── blank-nodes.ttl │ │ ├── sample.nq │ │ ├── sample.nt │ │ ├── sample.rdf.xml │ │ └── sample.ttl │ └── tests │ │ └── e2e │ │ ├── features │ │ └── insert.feature │ │ └── main.rs ├── axone-dataverse │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ ├── src │ │ ├── bin │ │ │ └── schema.rs │ │ ├── contract.rs │ │ ├── credential │ │ │ ├── crypto.rs │ │ │ ├── error.rs │ │ │ ├── mod.rs │ │ │ ├── proof.rs │ │ │ ├── rdf_marker.rs │ │ │ └── vc.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ ├── registrar │ │ │ ├── credential.rs │ │ │ ├── mod.rs │ │ │ ├── rdf.rs │ │ │ └── registry.rs │ │ ├── state.rs │ │ └── testutil.rs │ └── testdata │ │ ├── proof-ed255192020-ok.nq │ │ ├── proof-ed255192020-options.nq │ │ ├── proof-invalid-pkey.nq │ │ ├── proof-malformed-value.nq │ │ ├── proof-malformed.nq │ │ ├── proof-missing-created.nq │ │ ├── proof-missing-method.nq │ │ ├── proof-missing-purpose.nq │ │ ├── proof-missing-type.nq │ │ ├── proof-missing-value.nq │ │ ├── proof-unsupported.nq │ │ ├── vc-claim-hierarchy.nq │ │ ├── vc-di-ed-ok.nq │ │ ├── vc-ecdsa-2019-ok.nq │ │ ├── vc-eddsa-2018-ok.nq │ │ ├── vc-eddsa-2020-ok-unsecured-trusted.nq │ │ ├── vc-eddsa-2020-ok-unsecured.nq │ │ ├── vc-eddsa-2020-ok.nq │ │ ├── vc-unsupported-1.nq │ │ ├── vc-unsupported-2.nq │ │ ├── vc-unsupported-3.nq │ │ ├── vc-unsupported-4.nq │ │ └── vc-valid.nq ├── axone-law-stone │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ ├── examples │ │ ├── multiple-sources │ │ │ ├── README.md │ │ │ ├── gov.pl │ │ │ └── template.pl │ │ └── single-source │ │ │ ├── README.md │ │ │ └── gov.pl │ └── src │ │ ├── bin │ │ └── schema.rs │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── helper.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ └── state.rs └── axone-objectarium │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ ├── bin │ └── schema.rs │ ├── compress.rs │ ├── contract.rs │ ├── crypto.rs │ ├── cursor.rs │ ├── error.rs │ ├── lib.rs │ ├── msg.rs │ ├── pagination.rs │ └── state.rs ├── docs ├── axone-cognitarium.md ├── axone-dataverse.md ├── axone-law-stone.md └── axone-objectarium.md ├── etc ├── cognitarium.webp ├── dataverse.webp ├── law-stone.webp └── objectarium.webp ├── packages ├── axone-cognitarium-client │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ │ ├── client.rs │ │ └── lib.rs ├── axone-logic-bindings │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ │ ├── atom.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── query.rs │ │ ├── term_parser.rs │ │ └── testing │ │ ├── mock.rs │ │ └── mod.rs ├── axone-objectarium-client │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ │ ├── lib.rs │ │ └── object.rs ├── axone-rdf │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ │ ├── dataset.rs │ │ ├── lib.rs │ │ ├── normalize.rs │ │ ├── owned_model.rs │ │ ├── serde.rs │ │ └── uri.rs ├── axone-wasm │ ├── Cargo.toml │ ├── Makefile.toml │ ├── README.md │ └── src │ │ ├── error.rs │ │ ├── lib.rs │ │ └── uri.rs └── testing │ ├── Cargo.toml │ ├── Makefile.toml │ └── src │ ├── addr.rs │ ├── lib.rs │ └── mock.rs └── templates └── axone-smart-contract ├── .ffizer.yaml └── {{ contract_name }} ├── Cargo.ffizer.hbs.toml ├── Makefile.toml ├── README.ffizer.hbs.md └── src ├── bin └── schema.ffizer.hbs.rs ├── contract.rs ├── error.rs ├── lib.rs ├── msg.rs └── state.rs /.coderabbit.yaml: -------------------------------------------------------------------------------- 1 | language: "en-US" 2 | early_access: true 3 | tone_instructions: >- 4 | Maintain a formal tone, highlighting issues, and suggesting production-grade, elegant, and concise solutions. 5 | reviews: 6 | profile: "chill" 7 | request_changes_workflow: true 8 | high_level_summary: true 9 | poem: false 10 | review_status: true 11 | collapse_walkthrough: false 12 | path_filters: 13 | - "!docs/*.md" 14 | path_instructions: 15 | - path: "**/*.rs" 16 | instructions: >- 17 | Review the Rust code, point out issues relative to principles of clean 18 | code, expressiveness, and performance. 19 | 20 | Suggest idiomatic solutions and best practices. 21 | - path: "**/*.sh" 22 | instructions: >- 23 | Review the shell scripts, point out issues relative to security, 24 | performance, and maintainability. 25 | - path: "**/*.toml" 26 | instructions: >- 27 | Review the TOML configuration files for correctness, maintainability, 28 | and adherence to best practices. 29 | auto_review: 30 | enabled: true 31 | drafts: false 32 | auto_incremental_review: false # review on demand 33 | chat: 34 | auto_reply: true 35 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = *.json,*.ttl,CHANGELOG.md,./docs,./target 3 | count = true 4 | quiet-level = 3 5 | ignore-words-list = crate,ser 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | indent_size = 2 14 | 15 | [*.cjs] 16 | trim_trailing_whitespace = false 17 | indent_size = 2 18 | 19 | [*.{yml,yaml,json}] 20 | trim_trailing_whitespace = false 21 | indent_size = 2 22 | 23 | [*.toml] 24 | indent_size = 2 25 | indent_style = space 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | reviewers: 8 | - amimart 9 | - ccamel 10 | assignees: 11 | - amimart 12 | - ccamel 13 | - package-ecosystem: "cargo" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | open-pull-requests-limit: 5 18 | reviewers: 19 | - amimart 20 | - ccamel 21 | assignees: 22 | - amimart 23 | - ccamel 24 | -------------------------------------------------------------------------------- /.github/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contracts", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "cargo make wasm" 6 | }, 7 | "devDependencies": { 8 | "@size-limit/preset-app": "^7.0.8" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/add-to-project.yml: -------------------------------------------------------------------------------- 1 | name: Add to project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v1.0.2 14 | with: 15 | project-url: https://github.com/orgs/axone-protocol/projects/2 16 | github-token: ${{ secrets.OPS_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto merge 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | auto-merge-dependabot: 8 | runs-on: ubuntu-22.04 9 | if: github.actor == 'dependabot[bot]' 10 | steps: 11 | - name: Auto merge PR 12 | uses: ahmadnassri/action-dependabot-auto-merge@v2 13 | with: 14 | target: minor 15 | github-token: ${{ secrets.OPS_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_call: 5 | 6 | push: 7 | branches: [ main ] 8 | 9 | pull_request: 10 | branches: [ main ] 11 | 12 | concurrency: 13 | group: build-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build-wasm: 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - name: Check out repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Cache cargo registry 24 | uses: actions/cache@v4 25 | with: 26 | path: | 27 | ~/.cargo/registry 28 | ~/.cargo/git 29 | ~/.cargo/bin 30 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-cargo- 33 | 34 | - name: Setup rust 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: 1.81 38 | default: true 39 | override: true 40 | 41 | - name: Install cargo make 42 | uses: davidB/rust-cargo-make@v1 43 | 44 | - name: Build wasm 45 | run: cargo make wasm 46 | -------------------------------------------------------------------------------- /.github/workflows/notify-draft.yml: -------------------------------------------------------------------------------- 1 | name: Notify externals repositories 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | update-docs: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Update draft docs repository 12 | uses: fjogeleit/http-request-action@v1 13 | with: 14 | url: 'https://api.github.com/repos/axone-protocol/docs/actions/workflows/39152549/dispatches' 15 | method: 'POST' 16 | customHeaders: '{"Accept": "application/vnd.github+json", "Authorization": "Bearer ${{ secrets.OPS_TOKEN }}"}' 17 | data: |- 18 | { 19 | "ref": "main", 20 | "inputs": { 21 | "version": "main", 22 | "repository": "${{github.repository}}", 23 | "section": "contracts", 24 | "docs_directory": "docs/*", 25 | "draft": "true" 26 | } 27 | } 28 | 29 | update-schema: 30 | runs-on: ubuntu-22.04 31 | steps: 32 | - name: Update draft docs repository 33 | uses: fjogeleit/http-request-action@v1 34 | with: 35 | url: 'https://api.github.com/repos/axone-protocol/axone-contract-schema/actions/workflows/68383422/dispatches' 36 | method: 'POST' 37 | customHeaders: '{"Accept": "application/vnd.github+json", "Authorization": "Bearer ${{ secrets.OPS_TOKEN }}"}' 38 | data: |- 39 | { 40 | "ref": "main", 41 | "inputs": { 42 | "ref": "main", 43 | "draft": "true" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/notify-release.yml: -------------------------------------------------------------------------------- 1 | name: notify-release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: "Release tag (leave empty for last one)" 11 | 12 | jobs: 13 | set-env: 14 | runs-on: ubuntu-22.04 15 | outputs: 16 | tag: ${{ steps.set-env.outputs.tag }} 17 | repo_name: ${{ steps.set-env.outputs.repo_name }} 18 | steps: 19 | - name: Check out repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Expose tag and repo_name 23 | id: set-env 24 | run: | 25 | if [ -n "$INPUT_TAG" ]; then 26 | TAG="$INPUT_TAG" 27 | else 28 | TAG=$(gh release view --json tagName -q '.tagName') 29 | fi 30 | echo "tag=$TAG" >> $GITHUB_OUTPUT 31 | 32 | REPO_NAME=${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/} 33 | echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT 34 | env: 35 | INPUT_TAG: ${{ github.event.inputs.tag }} 36 | GH_TOKEN: ${{ secrets.OPS_TOKEN }} 37 | 38 | notify-github-discussion: 39 | runs-on: ubuntu-22.04 40 | needs: set-env 41 | steps: 42 | - name: Check out repository 43 | uses: actions/checkout@v4 44 | 45 | - name: Extract changelog for tag 46 | run: | 47 | { 48 | echo 'CHANGELOG<> "$GITHUB_ENV" 52 | env: 53 | GH_TOKEN: ${{ secrets.OPS_TOKEN }} 54 | 55 | - name: Create an announcement discussion for release 56 | uses: abirismyname/create-discussion@v2.1.0 57 | with: 58 | title: 🎉 ${{ needs.set-env.outputs.repo_name }} ${{ needs.set-env.outputs.tag }} released! 59 | body: | 60 | Hey frens! [${{ github.repository }}](https://github.com/${{ github.repository }}) `${{ needs.set-env.outputs.tag }}` just dropped! 🚀 61 | 62 | Some fresh updates are here. Dive into the changelog and see what's cooking! 🔥 63 | 64 | # Changelog 65 | 66 | ${{ env.CHANGELOG }} 67 | 68 | # Resources 69 | 70 | 📄 Changelog: 71 | 🛠️ Official repo: 72 | 💬 Vibe with us on Discord: <${{ env.DISCORD_URL }}> 73 | 🐦 Catch us on 𝕏: <${{ env.TWITTER_URL }}> 74 | repository-id: ${{ env.REPOSITORY_ID }} 75 | category-id: ${{ env.CATEGORY_ID }} 76 | env: 77 | GH_TOKEN: ${{ secrets.OPS_TOKEN }} 78 | DISCORD_URL: ${{ vars.DISCORD_URL }} 79 | TWITTER_URL: ${{ vars.TWITTER_URL }} 80 | REPOSITORY_ID: ${{ vars.DISCUSSIONS_REPOSITORY_ID }} 81 | CATEGORY_ID: ${{ vars.DISCUSSIONS_CATEGORY_ID }} 82 | 83 | update-docs: 84 | runs-on: ubuntu-22.04 85 | if: github.event_name != 'workflow_dispatch' 86 | steps: 87 | - name: Update docs repository 88 | uses: fjogeleit/http-request-action@v1 89 | with: 90 | url: "https://api.github.com/repos/axone-protocol/docs/actions/workflows/39152549/dispatches" 91 | method: "POST" 92 | customHeaders: '{"Accept": "application/vnd.github+json", "Authorization": "Bearer ${{ secrets.OPS_TOKEN }}"}' 93 | data: |- 94 | { 95 | "ref": "main", 96 | "inputs": { 97 | "version": "${{ github.event.release.tag_name }}", 98 | "repository": "${{github.repository}}", 99 | "section": "contracts", 100 | "docs_directory": "docs/*", 101 | "draft": "false" 102 | } 103 | } 104 | 105 | update-schema: 106 | runs-on: ubuntu-22.04 107 | if: github.event_name != 'workflow_dispatch' 108 | steps: 109 | - name: Update schema repository 110 | uses: fjogeleit/http-request-action@v1 111 | with: 112 | url: "https://api.github.com/repos/axone-protocol/axone-contract-schema/actions/workflows/68383422/dispatches" 113 | method: "POST" 114 | customHeaders: '{"Accept": "application/vnd.github+json", "Authorization": "Bearer ${{ secrets.OPS_TOKEN }}"}' 115 | data: |- 116 | { 117 | "ref": "main", 118 | "inputs": { 119 | "ref": "${{ github.event.release.tag_name }}", 120 | "draft": "false" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | concurrency: 8 | group: publish-${{ github.ref }} 9 | cancel-in-progress: false 10 | 11 | jobs: 12 | publish-crates: 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - name: Check out repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Cache cargo registry 19 | uses: actions/cache@v4 20 | with: 21 | path: | 22 | ~/.cargo/registry 23 | ~/.cargo/git 24 | ~/.cargo/bin 25 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-cargo- 28 | 29 | - name: Setup rust 30 | uses: actions-rs/toolchain@v1 31 | with: 32 | toolchain: 1.81 33 | default: true 34 | override: true 35 | 36 | - name: Install cargo make 37 | uses: davidB/rust-cargo-make@v1 38 | 39 | - name: Publish crates to crates.io 40 | env: 41 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 42 | run: | 43 | cargo make publish-crates 44 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | lint: 8 | if: github.ref == 'refs/heads/main' && github.actor == 'bot-anik' 9 | uses: ./.github/workflows/lint.yml 10 | 11 | build: 12 | if: github.ref == 'refs/heads/main' && github.actor == 'bot-anik' 13 | uses: ./.github/workflows/build.yml 14 | 15 | test: 16 | if: github.ref == 'refs/heads/main' && github.actor == 'bot-anik' 17 | uses: ./.github/workflows/test.yml 18 | 19 | perform-release: 20 | if: github.ref == 'refs/heads/main' && github.actor == 'bot-anik' 21 | needs: 22 | - lint 23 | - build 24 | - test 25 | runs-on: ubuntu-22.04 26 | steps: 27 | - name: Check out repository 28 | uses: actions/checkout@v4 29 | with: 30 | token: ${{ secrets.OPS_TOKEN }} 31 | 32 | - name: Import GPG key 33 | uses: crazy-max/ghaction-import-gpg@v6 34 | with: 35 | gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} 36 | passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }} 37 | git_config_global: true 38 | git_user_signingkey: true 39 | git_commit_gpgsign: true 40 | 41 | - name: Setup node 42 | uses: actions/setup-node@v4 43 | with: 44 | node-version: 20 45 | 46 | - name: Setup jq 47 | uses: dcarbone/install-jq-action@v3 48 | with: 49 | version: 1.7 50 | force: true 51 | 52 | - name: Setup rust 53 | uses: actions-rs/toolchain@v1 54 | with: 55 | toolchain: 1.81 56 | default: true 57 | override: true 58 | 59 | - name: Install cargo make 60 | uses: davidB/rust-cargo-make@v1 61 | 62 | - name: Release project 63 | uses: cycjimmy/semantic-release-action@v4 64 | with: 65 | semantic_version: 22.0.5 66 | branch: main 67 | extra_plugins: | 68 | @semantic-release/changelog 69 | @semantic-release/exec 70 | @semantic-release/git 71 | semantic-release-replace-plugin@1.2.7 72 | extends: | 73 | conventional-changelog-conventionalcommits@7.0.1 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.OPS_TOKEN }} 76 | GIT_AUTHOR_NAME: ${{ vars.BOT_GIT_AUTHOR_NAME }} 77 | GIT_AUTHOR_EMAIL: ${{ vars.BOT_GIT_AUTHOR_EMAIL }} 78 | GIT_COMMITTER_NAME: ${{ vars.BOT_GIT_COMMITTER_NAME }} 79 | GIT_COMMITTER_EMAIL: ${{ vars.BOT_GIT_COMMITTER_EMAIL }} 80 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_call: 5 | 6 | push: 7 | branches: [ main ] 8 | 9 | pull_request: 10 | branches: [ main ] 11 | 12 | concurrency: 13 | group: test-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | test-rust: 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - name: Check out repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Cache cargo registry 24 | uses: actions/cache@v4 25 | with: 26 | path: | 27 | ~/.cargo/registry 28 | ~/.cargo/git 29 | ~/.cargo/bin 30 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-cargo- 33 | 34 | - name: Setup rust 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: 1.81 38 | default: true 39 | override: true 40 | 41 | - name: Install cargo make 42 | uses: davidB/rust-cargo-make@v1 43 | 44 | - name: Test rust code 45 | run: cargo make test-coverage 46 | 47 | - name: Upload coverage 48 | uses: codecov/codecov-action@v5 49 | with: 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | files: ./lcov.info 52 | fail_ci_if_error: false 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | .vscode/ 4 | .DS_Store 5 | *.profraw 6 | *.lcov 7 | lcov.info 8 | contracts/*/schema/ 9 | artifacts 10 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | line-length: false 2 | no-hard-tabs: false 3 | no-inline-html: false 4 | no-duplicate-header: false 5 | -------------------------------------------------------------------------------- /.releaserc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | branches: ["main"], 3 | plugins: [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | preset: "conventionalcommits", 8 | releaseRules: [ 9 | { type: "build", scope: "deps", release: "patch" }, 10 | { type: "build", scope: "deps-dev", release: "patch" }, 11 | { type: "refactor", release: "patch" }, 12 | { type: "style", release: "patch" }, 13 | { type: "ci", release: "patch" }, 14 | { type: "chore", release: "patch" }, 15 | { type: "docs", release: "patch" }, 16 | { breaking: true, release: "major" }, 17 | ], 18 | }, 19 | ], 20 | [ 21 | "@semantic-release/release-notes-generator", 22 | { 23 | preset: "conventionalcommits", 24 | }, 25 | ], 26 | [ 27 | "@semantic-release/changelog", 28 | { 29 | changelogFile: "CHANGELOG.md", 30 | changelogTitle: "# AXONE contracts", 31 | }, 32 | ], 33 | [ 34 | "semantic-release-replace-plugin", 35 | { 36 | replacements: [ 37 | { 38 | files: ["Cargo.toml"], 39 | from: /^version\s+=\s+"\d+\.\d+\.\d+"$/gm, 40 | to: 'version = "${nextRelease.version}"', 41 | countMatches: true, 42 | results: [ 43 | { 44 | file: "Cargo.toml", 45 | hasChanged: true, 46 | numMatches: 1, 47 | numReplacements: 1, 48 | }, 49 | ], 50 | }, 51 | { 52 | files: ["Cargo.toml"], 53 | from: /((axone-[\w-]+)\s*=\s*\{\s*path\s*=\s*"\.\/[^"]*",\s+version\s+=\s+)"\d+\.\d+\.\d+"/g, 54 | to: ( 55 | _match, 56 | prefix, 57 | _dependencyName, 58 | _path, 59 | _extra, 60 | _version, 61 | context 62 | ) => `${prefix}"${context.nextRelease.version}"`, 63 | countMatches: true, 64 | results: [ 65 | { 66 | file: "Cargo.toml", 67 | hasChanged: true, 68 | numMatches: 7, 69 | numReplacements: 7, 70 | }, 71 | ], 72 | }, 73 | ], 74 | }, 75 | ], 76 | [ 77 | "@semantic-release/exec", 78 | { 79 | prepareCmd: 80 | "cargo make schema && cargo make docs-generate && cargo make release-wasm", 81 | }, 82 | ], 83 | [ 84 | "@semantic-release/github", 85 | { 86 | successComment: false, 87 | assets: [ 88 | { path: "./artifacts/axone_objectarium.wasm" }, 89 | { path: "./artifacts/axone_law_stone.wasm" }, 90 | { path: "./artifacts/axone_cognitarium.wasm" }, 91 | { path: "./artifacts/axone_dataverse.wasm" }, 92 | { path: "./artifacts/checksums.txt" }, 93 | { 94 | path: "./contracts/axone-objectarium/schema/axone-objectarium.json", 95 | }, 96 | { path: "./contracts/axone-law-stone/schema/axone-law-stone.json" }, 97 | { 98 | path: "./contracts/axone-cognitarium/schema/axone-cognitarium.json", 99 | }, 100 | { path: "./contracts/axone-dataverse/schema/axone-dataverse.json" }, 101 | ], 102 | }, 103 | ], 104 | [ 105 | "@semantic-release/git", 106 | { 107 | assets: [ 108 | "CHANGELOG.md", 109 | "docs/**", 110 | "Cargo.toml", 111 | "Cargo.lock", 112 | ], 113 | message: "chore(release): perform release ${nextRelease.version}", 114 | }, 115 | ], 116 | ], 117 | }; 118 | -------------------------------------------------------------------------------- /.size-limit.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "target/wasm32-unknown-unknown/release/axone_objectarium.wasm", 4 | "running": false, 5 | "brotli": false, 6 | "gzip": false 7 | }, 8 | { 9 | "path": "target/wasm32-unknown-unknown/release/axone_law_stone.wasm", 10 | "running": false, 11 | "brotli": false, 12 | "gzip": false 13 | }, 14 | { 15 | "path": "target/wasm32-unknown-unknown/release/axone_cognitarium.wasm", 16 | "running": false, 17 | "brotli": false, 18 | "gzip": false 19 | }, 20 | { 21 | "path": "target/wasm32-unknown-unknown/release/axone_dataverse.wasm", 22 | "running": false, 23 | "brotli": false, 24 | "gzip": false 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /.sort-derives.toml: -------------------------------------------------------------------------------- 1 | exclude = [] 2 | preserve = false 3 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/*.toml"] 2 | 3 | [formatting] 4 | align_entries = false 5 | reorder_keys = true 6 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | extends: default 2 | rules: 3 | document-start: disable 4 | line-length: 5 | max: 160 6 | level: warning 7 | brackets: 8 | min-spaces-inside: 0 9 | max-spaces-inside: 1 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["contracts/*", "packages/*"] 3 | resolver = "2" 4 | 5 | [workspace.package] 6 | authors = ["AXONE"] 7 | edition = "2021" 8 | homepage = "https://axone.xyz/" 9 | keywords = ["cosmwasm", "blockchain"] 10 | license = "BSD-3-Clause" 11 | repository = "https://github.com/axone-protocol/contracts" 12 | rust-version = "1.81" 13 | version = "7.0.0" 14 | 15 | [profile.release] 16 | codegen-units = 1 17 | debug = false 18 | debug-assertions = false 19 | incremental = false 20 | lto = true 21 | opt-level = 3 22 | overflow-checks = true 23 | panic = 'abort' 24 | rpath = false 25 | 26 | [workspace.dependencies] 27 | axone-cognitarium = { path = "./contracts/axone-cognitarium", version = "7.0.0", features = [ 28 | "library", 29 | ] } 30 | axone-cognitarium-client = { path = "./packages/axone-cognitarium-client", version = "7.0.0" } 31 | axone-logic-bindings = { path = "./packages/axone-logic-bindings", version = "7.0.0" } 32 | axone-objectarium = { path = "./contracts/axone-objectarium", version = "7.0.0", features = [ 33 | "library", 34 | ] } 35 | axone-objectarium-client = { path = "./packages/axone-objectarium-client", version = "7.0.0" } 36 | axone-rdf = { path = "./packages/axone-rdf", version = "7.0.0" } 37 | axone-wasm = { path = "./packages/axone-wasm", version = "7.0.0" } 38 | cosmwasm-schema = "2.2.2" 39 | cosmwasm-std = { version = "2.2.2", features = ["cosmwasm_2_2"] } 40 | cosmwasm-storage = "1.5.2" 41 | cw-multi-test = "2.2.0" 42 | cw-storage-plus = "2.0.0" 43 | cw-utils = "2.0.0" 44 | cw2 = "2.0.0" 45 | iref = "3.1.3" 46 | langtag = "0.3.4" 47 | rdf-types = "0.18.2" 48 | rio_api = "0.8.5" 49 | rio_turtle = "0.8.5" 50 | rio_xml = "0.8.5" 51 | schemars = "0.8.22" 52 | serde = { version = "1.0.219", default-features = false, features = ["derive"] } 53 | serde-json-wasm = "1.0.1" 54 | testing = { path = "packages/testing" } 55 | thiserror = { version = "2.0.12" } 56 | -------------------------------------------------------------------------------- /Cranky.toml: -------------------------------------------------------------------------------- 1 | allow = [] 2 | 3 | deny = [ 4 | "clippy::complexity", 5 | "clippy::expect_used", 6 | "clippy::implicit_clone", 7 | "clippy::manual_string_new", 8 | "clippy::map_unwrap_or", 9 | "clippy::match_same_arms", 10 | "clippy::option_as_ref_deref", 11 | "clippy::option_filter_map", 12 | "clippy::option_map_unit_fn", 13 | "clippy::or_then_unwrap", 14 | "clippy::panic", 15 | "clippy::range_plus_one", 16 | "clippy::redundant_clone", 17 | "clippy::redundant_closure_for_method_calls", 18 | "clippy::semicolon_if_nothing_returned", 19 | "clippy::single_match_else", 20 | "clippy::unnecessary_cast", 21 | "clippy::unnecessary_wraps", 22 | "clippy::unused_self", 23 | "clippy::unwrap_used", 24 | "clippy::useless_asref", 25 | "clippy::useless_conversion", 26 | "clippy::useless_format", 27 | "nonstandard-style", 28 | "rust_2018_idioms", 29 | "rust-2021-compatibility", 30 | "trivial_casts", 31 | "trivial_numeric_casts", 32 | "unsafe_code", 33 | "unused_import_braces", 34 | "unused_lifetimes", 35 | "unused_qualifications", 36 | "warnings", 37 | ] 38 | 39 | warn = [] 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, AXONE.com 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.81.0" 2 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | description = "A CosmWasm Smart Contract which enables the storage and querying of Semantic data using RDF, which represents information as semantic triples." 4 | edition = { workspace = true } 5 | homepage = { workspace = true } 6 | keywords = { workspace = true } 7 | license = { workspace = true } 8 | name = "axone-cognitarium" 9 | repository = { workspace = true } 10 | rust-version = { workspace = true } 11 | version = { workspace = true } 12 | 13 | exclude = [ 14 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 15 | "contract.wasm", 16 | "hash.txt", 17 | ] 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [lib] 22 | crate-type = ["cdylib", "rlib"] 23 | 24 | [dependencies] 25 | axone-rdf.workspace = true 26 | blake3 = "1.8.2" 27 | cosmwasm-schema.workspace = true 28 | cosmwasm-std.workspace = true 29 | cw-storage-plus.workspace = true 30 | cw-utils.workspace = true 31 | cw2.workspace = true 32 | derive_builder = "0.20.2" 33 | either = "1.15.0" 34 | rio_api.workspace = true 35 | rio_turtle.workspace = true 36 | rio_xml.workspace = true 37 | schemars.workspace = true 38 | serde.workspace = true 39 | thiserror.workspace = true 40 | 41 | [dev-dependencies] 42 | base64 = "0.22.1" 43 | cucumber = "0.21.1" 44 | futures = "0.3.31" 45 | serde_yaml = "0.9.34" 46 | testing.workspace = true 47 | 48 | [features] 49 | # use library feature to disable all instantiate/execute/query exports 50 | library = [] 51 | 52 | [[test]] 53 | harness = false 54 | name = "cucumber" 55 | path = "tests/e2e/main.rs" 56 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.generate_schema] 2 | args = ["run", "--bin", "schema"] 3 | command = "cargo" 4 | 5 | [tasks.schema] 6 | dependencies = ["generate_schema"] 7 | script = ''' 8 | SCHEMA=$(find schema -type f -maxdepth 1 -name '*.json' -print0) 9 | TITLE=$(jq -r .contract_name $SCHEMA) 10 | jq --arg description "$(cat README.md)" '. + {description: $description}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 11 | jq --arg title $TITLE '. + {title: $title}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 12 | ''' 13 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/examples/prolog-query/README.md: -------------------------------------------------------------------------------- 1 | # Prolog query 2 | 3 | In this example we'll see how to query the `axone-cognitarium` from Prolog programs executed by the AXONE logic module. 4 | 5 | We'll use for that purpose the [query.pl](query.pl) sample program, multiple predicates are defined in it, we'll explore them step by step. 6 | 7 | The query we'll make will be performed against a `axone-cognitarium` instance filled with the provided [sample data](../sample-data.rdf.xml), see the [basic example](../basic) to insert them. 8 | 9 | ## Forge the CosmWasm query 10 | 11 | As seen in a [axone-law-stone example](../../../axone-law-stone/examples/multiple-sources), interaction with smart contracts from Prolog is based on the 12 | interpreter virtual filesystem that'll handle dedicated cosmwasm URIs. 13 | 14 | It's worth to mention that to query cosmwasm smart contracts and getting the raw response we'll need to set in the related URI the param `base64Decode` to `false`. 15 | 16 | The `cosmwasm_query` predicate will help to create the cosmwasm URI, for example: 17 | 18 | ```bash 19 | axoned query logic ask \ 20 | --program-file query.pl \ 21 | "cosmwasm_query(cognitarium, '${CONTRACT_ADDR}', json([key-value]), false, URI)." 22 | ``` 23 | 24 | ## Call the smart contract 25 | 26 | By calling the `cosmwasm_call` predicate with a cosmwasm URI we'll be able to get the JSON response, let's try it with a simple `axone-cognitarium` `Store` query which returns usage information about the triple store: 27 | 28 | ```bash 29 | axoned query logic ask \ 30 | --program-file query.pl \ 31 | "cosmwasm_query(cognitarium, '${CONTRACT_ADDR}', 'store', false, URI), cosmwasm_call(URI, Response)." 32 | ``` 33 | 34 | ## Select query 35 | 36 | Through the `cognitarium_dataset_tags`, we can query the tags present in metadata describing a specific dataset, for example: 37 | 38 | ```bash 39 | axoned query logic ask \ 40 | --program-file query.pl \ 41 | "cognitarium_dataset_tags('${CONTRACT_ADDR}', 'https://ontology.axone.space/dataverse/dataset/0ea1fc7a-dd97-4adc-a10e-169c6597bcde', Tags)." 42 | ``` 43 | 44 | ## Exploiting the response 45 | 46 | Using the `cognitarium_dataset_has_tag` predicate we show how to define rules based on the contract response, here on the present of a certain tag: 47 | 48 | ```bash 49 | axoned query logic ask \ 50 | --program-file query.pl \ 51 | "cognitarium_dataset_has_tag('${CONTRACT_ADDR}', 'https://ontology.axone.space/dataverse/dataset/0ea1fc7a-dd97-4adc-a10e-169c6597bcde', 'AwesomeData')." 52 | ``` 53 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/examples/prolog-query/query.pl: -------------------------------------------------------------------------------- 1 | fold_left(Goal, [H|T], Result) :- 2 | fold_left(Goal, T, H, Result). 3 | 4 | fold_left(Goal, [H|T], Acc, Result) :- 5 | call(Goal, Acc, H, NewAcc), 6 | fold_left(Goal, T, NewAcc, Result). 7 | 8 | fold_left(_Goal, [], Result, Result). 9 | 10 | % Concatenate all the elements in the list using the atom_concat predicate. 11 | atoms_concat(List, Result) :- 12 | fold_left(atom_concat, List, Result). 13 | 14 | % Forge a cosmwasm URI that can be handled by the logic module. Take care of the query json serialization & url encoding. 15 | cosmwasm_query(ContractName, ContractAddr, Query, DecodeBase64, URI) :- 16 | json_prolog(RawQuery, Query), 17 | uri_encoded(query, RawQuery, EncodedQuery), 18 | atoms_concat([ 19 | 'cosmwasm:', 20 | ContractName, 21 | ':', 22 | ContractAddr, 23 | '?query=', 24 | EncodedQuery, 25 | '&base64Decode=', 26 | DecodeBase64 27 | ], URI). 28 | 29 | % Execute the cosmwasm query by opening the URI stream relying on the logic module interpreter virtual filesystem. 30 | % It then consumes the stream unifying it with its string reprensation, close the stream and make the json conversion of 31 | % the contract response. 32 | cosmwasm_call(URI, Response) :- 33 | open(URI, 'read', Stream), 34 | read_string(Stream, _, Raw), 35 | close(Stream), 36 | json_prolog(Raw, Response). 37 | 38 | % Represents the cognitarium Select query input. 39 | cognitarium_select(Prefixes, Select, Where, Limit, Query) :- 40 | Query = json([ 41 | select-json([ 42 | query-json([ 43 | prefixes-Prefixes, 44 | select-Select, 45 | where-Where, 46 | limit-Limit 47 | ]) 48 | ]) 49 | ]). 50 | 51 | % Extract the bindings field of a cognitarium Select query response. 52 | cognitarium_select_bindings(SelectResponse, Bindings) :- 53 | SelectResponse = json([head-_,results-json([bindings-Bindings])]). 54 | 55 | % Extract the tag variable value from a single Select response binding. 56 | cognitarium_extract_binding_tag(Binding, Tag) :- 57 | Binding = json([tag-json([datatype- @(null),type-literal,value-Tag,'xml:lang'- @(null)])]). 58 | 59 | % Given a cognitarium address and a dataset identifier, resolves the tags contained in any metadata of the type GeneralMetadata. 60 | cognitarium_dataset_tags(CognitariumAddr, DatasetDID, Tags) :- 61 | cognitarium_select( 62 | [ 63 | json([prefix-'rdf', namespace-'http://www.w3.org/1999/02/22-rdf-syntax-ns#']), 64 | json([prefix-'core', namespace-'https://ontology.axone.space/core/']), 65 | json([prefix-'meta', namespace-'https://ontology.axone.space/metadata/dataset/']) 66 | ], 67 | [ 68 | json([variable-'tag']) 69 | ], 70 | [ 71 | json([ 72 | simple-json([ 73 | triple_pattern-json([ 74 | subject-json([variable-'meta']), 75 | predicate-json([node-json([named_node-json([prefixed-'core:describes'])])]), 76 | object-json([node-json([named_node-json([full-DatasetDID])])]) 77 | ]) 78 | ]) 79 | ]), 80 | json([ 81 | simple-json([ 82 | triple_pattern-json([ 83 | subject-json([variable-'meta']), 84 | predicate-json([node-json([named_node-json([prefixed-'rdf:type'])])]), 85 | object-json([node-json([named_node-json([prefixed-'meta:GeneralMetadata'])])]) 86 | ]) 87 | ]) 88 | ]), 89 | json([ 90 | simple-json([ 91 | triple_pattern-json([ 92 | subject-json([variable-'meta']), 93 | predicate-json([node-json([named_node-json([prefixed-'core:hasTag'])])]), 94 | object-json([variable-'tag']) 95 | ]) 96 | ]) 97 | ]) 98 | ], 99 | @(null), 100 | Query 101 | ), 102 | cosmwasm_query(cognitarium, CognitariumAddr, Query, false, URI), 103 | cosmwasm_call(URI, Response), 104 | cognitarium_select_bindings(Response, Bindings), 105 | maplist(cognitarium_extract_binding_tag, Bindings, Tags). 106 | 107 | % True if a given dataset identifier has the given tag through a GeneralMetadata in the provided cognitarium address. 108 | cognitarium_dataset_has_tag(CognitariumAddr, DatasetDID, Tag) :- 109 | cognitarium_dataset_tags(CognitariumAddr, DatasetDID, Tags), 110 | member(Tag, Tags). 111 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/examples/sample-data.rdf.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | A test Data Space. 14 | Un Data Space de test. 15 | AXONE 16 | 17 | AXONE 18 | Test 19 | 20 | Data Space de test 21 | Test Data Space 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | AXONE 34 | 35 | 36 | 37 | Tutorial 38 | AwesomeData 39 | Dataset de test 40 | test Dataset 41 | Me 42 | 43 | A test Dataset. 44 | Un Dataset de test. 45 | 46 | 47 | 48 | 49 | 50 | 51 | 2023-03-28T00:00:00+00:00 52 | 53 | 2023-03-28T00:00:00+00:00 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | AXONE 67 | 68 | 69 | 70 | Tutorial 71 | NotAwesomeData 72 | Dataset de tuto pas bo 73 | ugly tutorial Dataset 74 | Me 75 | 76 | An ugly tutorial Dataset. 77 | Un Dataset de tuto pas bo. 78 | 79 | 80 | 81 | 82 | 83 | 84 | 2023-03-28T00:00:00+00:00 85 | 86 | 2023-03-28T00:00:00+00:00 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use axone_cognitarium::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | execute: ExecuteMsg, 9 | query: QueryMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{StdError, Uint128}; 2 | use cw_utils::PaymentError; 3 | use rio_turtle::TurtleError; 4 | use rio_xml::RdfXmlError; 5 | use thiserror::Error; 6 | 7 | #[derive(Debug, Error, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("{0}")] 13 | ParseRDF(#[from] RDFParseError), 14 | 15 | #[error("{0}")] 16 | FormatRDF(String), 17 | 18 | #[error("{0}")] 19 | Store(#[from] StoreError), 20 | 21 | #[error("Only the owner can perform this operation.")] 22 | Unauthorized, 23 | 24 | #[error("{0}")] 25 | Payment(#[from] PaymentError), 26 | } 27 | 28 | impl From for ContractError { 29 | fn from(value: RdfXmlError) -> Self { 30 | RDFParseError::from(value).into() 31 | } 32 | } 33 | 34 | impl From for ContractError { 35 | fn from(value: TurtleError) -> Self { 36 | RDFParseError::from(value).into() 37 | } 38 | } 39 | 40 | #[derive(Debug, Eq, Error, PartialEq)] 41 | pub enum StoreError { 42 | #[error("Maximum triples number exceeded: {0}")] 43 | TripleCount(Uint128), 44 | 45 | #[error("Maximum byte size exceeded: {0}")] 46 | ByteSize(Uint128), 47 | 48 | #[error("Maximum triple byte size exceeded: {0} / {1}")] 49 | TripleByteSize(Uint128, Uint128), 50 | 51 | #[error("Maximum insert byte size exceeded: {0}")] 52 | InsertDataByteSize(Uint128), 53 | 54 | #[error("Maximum insert triple count exceeded: {0}")] 55 | InsertDataTripleCount(Uint128), 56 | } 57 | 58 | #[derive(Debug, Eq, Error, PartialEq)] 59 | pub enum RDFParseError { 60 | #[error("Error parsing XML RDF: {0}")] 61 | Xml(String), 62 | 63 | #[error("Error parsing Turtle RDF: {0}")] 64 | Turtle(String), 65 | } 66 | 67 | impl From for RDFParseError { 68 | fn from(value: RdfXmlError) -> Self { 69 | RDFParseError::Xml(value.to_string()) 70 | } 71 | } 72 | 73 | impl From for RDFParseError { 74 | fn from(value: TurtleError) -> Self { 75 | RDFParseError::Xml(value.to_string()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod parser; 5 | mod querier; 6 | mod rdf; 7 | pub mod state; 8 | mod storer; 9 | 10 | pub use crate::error::ContractError; 11 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | 3 | pub use crate::parser::ast::*; 4 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/querier/mapper.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{Literal, IRI}; 2 | use crate::state; 3 | use crate::state::{NamespaceSolver, Object}; 4 | use axone_rdf::uri::{expand_uri, explode_iri}; 5 | use cosmwasm_std::StdResult; 6 | use std::collections::HashMap; 7 | 8 | pub fn literal_as_object( 9 | ns_solver: &mut dyn NamespaceSolver, 10 | prefixes: &HashMap, 11 | literal: Literal, 12 | ) -> StdResult { 13 | Ok(Object::Literal(match literal { 14 | Literal::Simple(value) => state::Literal::Simple { value }, 15 | Literal::LanguageTaggedString { value, language } => { 16 | state::Literal::I18NString { value, language } 17 | } 18 | Literal::TypedValue { value, datatype } => state::Literal::Typed { 19 | value, 20 | datatype: iri_as_node(ns_solver, prefixes, datatype)?, 21 | }, 22 | })) 23 | } 24 | 25 | pub fn iri_as_node( 26 | ns_solver: &mut dyn NamespaceSolver, 27 | prefixes: &HashMap, 28 | iri: IRI, 29 | ) -> StdResult { 30 | match iri { 31 | IRI::Prefixed(prefixed) => expand_uri(&prefixed, prefixes), 32 | IRI::Full(full) => Ok(full), 33 | } 34 | .and_then(|iri| explode_iri(&iri)) 35 | .and_then(|(ns_key, v)| { 36 | ns_solver.resolve_from_val(ns_key).map(|ns| state::Node { 37 | namespace: ns.key, 38 | value: v, 39 | }) 40 | }) 41 | } 42 | 43 | pub fn iri_as_string(iri: IRI, prefixes: &HashMap) -> StdResult { 44 | match iri { 45 | IRI::Prefixed(prefixed) => expand_uri(&prefixed, prefixes), 46 | IRI::Full(full) => Ok(full), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/querier/mod.rs: -------------------------------------------------------------------------------- 1 | mod engine; 2 | mod expression; 3 | mod mapper; 4 | mod plan; 5 | mod plan_builder; 6 | mod variable; 7 | 8 | pub use engine::*; 9 | pub use plan::*; 10 | pub use plan_builder::*; 11 | pub use variable::ResolvedVariables; 12 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/rdf/atom.rs: -------------------------------------------------------------------------------- 1 | use rio_api::model::{Literal, NamedNode, Triple}; 2 | use std::fmt; 3 | 4 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 5 | pub enum Subject { 6 | NamedNode(String), 7 | BlankNode(String), 8 | } 9 | 10 | impl fmt::Display for Subject { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | match self { 13 | Subject::NamedNode(s) | Subject::BlankNode(s) => write!(f, "{s}"), 14 | } 15 | } 16 | } 17 | 18 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 19 | pub struct Property(pub String); 20 | 21 | impl fmt::Display for Property { 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | write!(f, "{}", self.0) 24 | } 25 | } 26 | 27 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 28 | pub enum Value { 29 | NamedNode(String), 30 | BlankNode(String), 31 | LiteralSimple(String), 32 | LiteralLang(String, String), 33 | LiteralDatatype(String, String), 34 | } 35 | 36 | impl fmt::Display for Value { 37 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 | match self { 39 | Value::NamedNode(s) | Value::BlankNode(s) | Value::LiteralSimple(s) => write!(f, "{s}"), 40 | Value::LiteralLang(s, l) => write!(f, "{s}@{l}"), 41 | Value::LiteralDatatype(s, d) => write!(f, "{s}^^{d}"), 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 47 | pub struct Atom { 48 | pub subject: Subject, 49 | pub property: Property, 50 | pub value: Value, 51 | } 52 | 53 | impl fmt::Display for Atom { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | write!(f, "<{}> <{}> '{}'", self.subject, self.property, self.value) 56 | } 57 | } 58 | 59 | impl<'a> From<&'a Atom> for Triple<'a> { 60 | fn from(atom: &'a Atom) -> Self { 61 | Triple { 62 | subject: match &atom.subject { 63 | Subject::NamedNode(s) | Subject::BlankNode(s) => NamedNode { iri: s.as_str() }, 64 | } 65 | .into(), 66 | predicate: NamedNode { 67 | iri: &atom.property.0, 68 | }, 69 | object: match &atom.value { 70 | Value::NamedNode(s) | Value::BlankNode(s) => NamedNode { iri: s.as_str() }.into(), 71 | Value::LiteralSimple(s) => Literal::Simple { value: s.as_str() }.into(), 72 | Value::LiteralLang(s, l) => Literal::LanguageTaggedString { 73 | value: s, 74 | language: l, 75 | } 76 | .into(), 77 | Value::LiteralDatatype(s, d) => Literal::Typed { 78 | value: s, 79 | datatype: NamedNode { iri: d }, 80 | } 81 | .into(), 82 | }, 83 | } 84 | } 85 | } 86 | 87 | #[cfg(test)] 88 | mod tests { 89 | use super::*; 90 | 91 | #[test] 92 | fn proper_display() { 93 | struct TC<'a> { 94 | input: Box, 95 | expected: String, 96 | } 97 | let cases = vec![ 98 | // # Subject 99 | TC { 100 | input: Box::new(Subject::BlankNode("blank".into())), 101 | expected: "blank".into(), 102 | }, 103 | TC { 104 | input: Box::new(Subject::NamedNode("named".into())), 105 | expected: "named".into(), 106 | }, 107 | // # Property 108 | TC { 109 | input: Box::new(Property("foo".into())), 110 | expected: "foo".into(), 111 | }, 112 | // # Value 113 | TC { 114 | input: Box::new(Value::NamedNode("named".into())), 115 | expected: "named".into(), 116 | }, 117 | TC { 118 | input: Box::new(Value::BlankNode("blank".into())), 119 | expected: "blank".into(), 120 | }, 121 | TC { 122 | input: Box::new(Value::LiteralSimple("simple".into())), 123 | expected: "simple".into(), 124 | }, 125 | TC { 126 | input: Box::new(Value::LiteralLang("lang".into(), "en".into())), 127 | expected: "lang@en".into(), 128 | }, 129 | TC { 130 | input: Box::new(Value::LiteralDatatype("data".into(), "uri".into())), 131 | expected: "data^^uri".into(), 132 | }, 133 | // # Atom 134 | TC { 135 | input: Box::new(Atom { 136 | subject: Subject::NamedNode("subject".into()), 137 | property: Property("predicate".into()), 138 | value: Value::LiteralLang("object".into(), "en".into()), 139 | }), 140 | expected: " 'object@en'".into(), 141 | }, 142 | ]; 143 | for tc in cases { 144 | assert_eq!(format!("{}", tc.input), tc.expected); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/rdf/mapper.rs: -------------------------------------------------------------------------------- 1 | use crate::parser; 2 | use crate::rdf::{Property, Subject, Value}; 3 | use axone_rdf::uri::expand_uri; 4 | use cosmwasm_std::StdError; 5 | use std::collections::HashMap; 6 | 7 | impl TryFrom<(parser::Node, &HashMap)> for Subject { 8 | type Error = StdError; 9 | 10 | fn try_from( 11 | (node, prefixes): (parser::Node, &HashMap), 12 | ) -> Result { 13 | match node { 14 | parser::Node::BlankNode(id) => Ok(Subject::BlankNode(id)), 15 | parser::Node::NamedNode(parser::IRI::Full(uri)) => Ok(Subject::NamedNode(uri)), 16 | parser::Node::NamedNode(parser::IRI::Prefixed(curie)) => { 17 | Ok(Subject::NamedNode(expand_uri(&curie, prefixes)?)) 18 | } 19 | } 20 | } 21 | } 22 | 23 | impl TryFrom<(parser::IRI, &HashMap)> for Property { 24 | type Error = StdError; 25 | 26 | fn try_from( 27 | (iri, prefixes): (parser::IRI, &HashMap), 28 | ) -> Result { 29 | match iri { 30 | parser::IRI::Full(uri) => Ok(Property(uri)), 31 | parser::IRI::Prefixed(curie) => Ok(Property(expand_uri(&curie, prefixes)?)), 32 | } 33 | } 34 | } 35 | 36 | impl TryFrom<(parser::Node, &HashMap)> for Value { 37 | type Error = StdError; 38 | 39 | fn try_from( 40 | (node, prefixes): (parser::Node, &HashMap), 41 | ) -> Result { 42 | match node { 43 | parser::Node::NamedNode(parser::IRI::Full(uri)) => Ok(Value::NamedNode(uri)), 44 | parser::Node::NamedNode(parser::IRI::Prefixed(curie)) => { 45 | Ok(Value::NamedNode(expand_uri(&curie, prefixes)?)) 46 | } 47 | parser::Node::BlankNode(id) => Ok(Value::BlankNode(id)), 48 | } 49 | } 50 | } 51 | 52 | impl TryFrom<(parser::Literal, &HashMap)> for Value { 53 | type Error = StdError; 54 | 55 | fn try_from( 56 | (literal, prefixes): (parser::Literal, &HashMap), 57 | ) -> Result { 58 | match literal { 59 | parser::Literal::Simple(value) => Ok(Value::LiteralSimple(value)), 60 | parser::Literal::LanguageTaggedString { value, language } => { 61 | Ok(Value::LiteralLang(value, language)) 62 | } 63 | parser::Literal::TypedValue { 64 | value, 65 | datatype: parser::IRI::Full(uri), 66 | } => Ok(Value::LiteralDatatype(value, uri)), 67 | parser::Literal::TypedValue { 68 | value, 69 | datatype: parser::IRI::Prefixed(prefix), 70 | } => Ok(Value::LiteralDatatype( 71 | value, 72 | expand_uri(&prefix, prefixes)?, 73 | )), 74 | } 75 | } 76 | } 77 | 78 | #[derive(Default)] 79 | pub struct PrefixMap(HashMap); 80 | impl PrefixMap { 81 | pub fn into_inner(self) -> HashMap { 82 | self.0 83 | } 84 | } 85 | 86 | impl From> for PrefixMap { 87 | fn from(as_list: Vec) -> Self { 88 | PrefixMap( 89 | as_list 90 | .into_iter() 91 | .map(|prefix| (prefix.prefix, prefix.namespace)) 92 | .collect(), 93 | ) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/rdf/mod.rs: -------------------------------------------------------------------------------- 1 | mod atom; 2 | mod mapper; 3 | 4 | pub use self::atom::*; 5 | pub use self::mapper::*; 6 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/state/blank_nodes.rs: -------------------------------------------------------------------------------- 1 | use cw_storage_plus::Item; 2 | 3 | /// A counter serving as blank node unique identifier generator. 4 | pub const BLANK_NODE_IDENTIFIER_COUNTER: Item = Item::new("blank_node_key"); 5 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | mod blank_nodes; 2 | mod namespaces; 3 | mod store; 4 | mod triples; 5 | 6 | pub use blank_nodes::*; 7 | pub use namespaces::*; 8 | pub use store::*; 9 | pub use triples::*; 10 | 11 | #[cfg(test)] 12 | mod test_util; 13 | #[cfg(test)] 14 | pub use test_util::*; 15 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/state/store.rs: -------------------------------------------------------------------------------- 1 | use crate::msg; 2 | use crate::msg::StoreResponse; 3 | use cosmwasm_std::{Addr, Uint128}; 4 | use cw_storage_plus::Item; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | pub const STORE: Item = Item::new("store"); 8 | 9 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 10 | pub struct Store { 11 | pub owner: Addr, 12 | pub limits: StoreLimits, 13 | pub stat: StoreStat, 14 | } 15 | 16 | impl Store { 17 | pub fn new(owner: Addr, limits: StoreLimits) -> Store { 18 | Store { 19 | owner, 20 | limits, 21 | stat: StoreStat::default(), 22 | } 23 | } 24 | } 25 | 26 | impl From for StoreResponse { 27 | fn from(value: Store) -> Self { 28 | Self { 29 | owner: value.owner.into(), 30 | limits: value.limits.into(), 31 | stat: value.stat.into(), 32 | } 33 | } 34 | } 35 | 36 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 37 | pub struct StoreLimits { 38 | pub max_triple_count: Uint128, 39 | pub max_byte_size: Uint128, 40 | pub max_triple_byte_size: Uint128, 41 | pub max_query_limit: u32, 42 | pub max_query_variable_count: u32, 43 | pub max_insert_data_byte_size: Uint128, 44 | pub max_insert_data_triple_count: Uint128, 45 | } 46 | 47 | impl From for StoreLimits { 48 | fn from(value: msg::StoreLimitsInput) -> Self { 49 | StoreLimits { 50 | max_triple_count: value.max_triple_count, 51 | max_byte_size: value.max_byte_size, 52 | max_triple_byte_size: value.max_triple_byte_size, 53 | max_query_limit: value.max_query_limit, 54 | max_query_variable_count: value.max_query_variable_count, 55 | max_insert_data_byte_size: value.max_insert_data_byte_size, 56 | max_insert_data_triple_count: value.max_insert_data_triple_count, 57 | } 58 | } 59 | } 60 | 61 | impl From for msg::StoreLimits { 62 | fn from(value: StoreLimits) -> Self { 63 | msg::StoreLimits { 64 | max_triple_count: value.max_triple_count, 65 | max_byte_size: value.max_byte_size, 66 | max_triple_byte_size: value.max_triple_byte_size, 67 | max_query_limit: value.max_query_limit, 68 | max_query_variable_count: value.max_query_variable_count, 69 | max_insert_data_byte_size: value.max_insert_data_byte_size, 70 | max_insert_data_triple_count: value.max_insert_data_triple_count, 71 | } 72 | } 73 | } 74 | 75 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] 76 | pub struct StoreStat { 77 | pub triple_count: Uint128, 78 | pub namespace_count: Uint128, 79 | pub byte_size: Uint128, 80 | } 81 | 82 | impl From for msg::StoreStat { 83 | fn from(value: StoreStat) -> Self { 84 | Self { 85 | triple_count: value.triple_count, 86 | namespace_count: value.namespace_count, 87 | byte_size: value.byte_size, 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/state/test_util.rs: -------------------------------------------------------------------------------- 1 | use crate::state::{Namespace, NamespaceSolver}; 2 | use cosmwasm_std::{StdError, StdResult}; 3 | use std::collections::BTreeMap; 4 | 5 | pub struct InMemoryNamespaceSolver { 6 | by_val: BTreeMap, 7 | by_key: BTreeMap, 8 | } 9 | 10 | impl InMemoryNamespaceSolver { 11 | pub fn with(namespaces: Vec<(u128, &str)>) -> Self { 12 | let mut by_val = BTreeMap::new(); 13 | let mut by_key = BTreeMap::new(); 14 | for (key, value) in namespaces { 15 | let ns = Namespace { 16 | value: value.to_string(), 17 | key, 18 | counter: 1, 19 | }; 20 | by_val.insert(value.to_string(), ns.clone()); 21 | by_key.insert(key, ns); 22 | } 23 | Self { by_val, by_key } 24 | } 25 | } 26 | 27 | impl NamespaceSolver for InMemoryNamespaceSolver { 28 | fn resolve_from_key(&mut self, key: u128) -> StdResult { 29 | self.by_key 30 | .get(&key) 31 | .ok_or_else(|| StdError::not_found("Namespace")) 32 | .cloned() 33 | } 34 | 35 | fn resolve_from_val(&mut self, _value: String) -> StdResult { 36 | self.by_val 37 | .get(&_value) 38 | .ok_or_else(|| StdError::not_found("Namespace")) 39 | .cloned() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/src/storer/mod.rs: -------------------------------------------------------------------------------- 1 | mod engine; 2 | 3 | pub use engine::*; 4 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/testdata/blank-nodes.ttl: -------------------------------------------------------------------------------- 1 | @prefix area: . 2 | @prefix core: . 3 | @prefix dataset: . 4 | @prefix license: . 5 | @prefix mediatype: . 6 | @prefix owl: . 7 | @prefix topic: . 8 | @prefix xsd: . 9 | 10 | a owl:NamedIndividual, 11 | ; 12 | core:describes dataset:7ff3d2a4-e6b2-4b06-8619-4fc8740dad86 ; 13 | core:hasCreator "IGN" ; 14 | core:hasDescription "ADMIN EXPRESS permet d'effectuer des croisements avec d'autres sources de données dans le but de construire des représentations thématiques du territoire selon une granularité administrative (commune, arrondissement départementaux, département, région). ADMIN EXPRESS est décliné dans une édition \"COG\", conforme au code officiel géographique publié chaque année par l'INSEE."@fr ; 15 | core:hasDescription "ADMIN EXPRESS allows cross-referencing with other data sources in order to build thematic representations of the territory according to an administrative granularity (commune, departmental district, department, region). ADMIN EXPRESS is available in a \"COG\" edition, in accordance with the official geographic code published each year by INSEE."@en ; 16 | core:hasDescription "ADMIN EXPRESS ermöglicht es, Kreuzungen mit anderen Datenquellen vorzunehmen, um thematische Darstellungen des Gebiets nach administrativer Granularität (Gemeinde, Departementsbezirke, Departement, Region) zu erstellen. ADMIN EXPRESS ist in einer \"COG\"-Ausgabe erhältlich, die dem offiziellen geografischen Code entspricht, der jedes Jahr vom INSEE veröffentlicht wird."@de ; 17 | core:hasFormat mediatype:application_vnd-shp ; 18 | core:hasImage ; 19 | core:hasLicense license:LO-FR-2_0 ; 20 | core:hasPublisher "AXONE" ; 21 | core:hasSpatialCoverage area:250 ; 22 | core:hasTag "INSEE", 23 | "commune", 24 | "france", 25 | "open data", 26 | "territoire" ; 27 | core:hasTemporalCoverage [ 28 | a owl:NamedIndividual, core:Period ; 29 | core:hasStartDate "2022-01-01T00:00:00+00:00"^^xsd:dateTime 30 | ] ; 31 | core:hasInformations [ 32 | core:hasInformation "this is a dataset" 33 | ] ; 34 | core:hasTitle "ADMIN EXPRESS COG 2022 COMMUNE"@fr ; 35 | core:hasTitle "ADMIN EXPRESS COG 2022 CITY"@en ; 36 | core:hasTitle "ADMIN EXPRESS COG 2022 GEMEINDE"@de ; 37 | core:hasTopic topic:Other . 38 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/testdata/sample.rdf.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | A test Data Space. 14 | Un Data Space de test. 15 | AXONE 16 | 17 | AXONE 18 | Test 19 | 20 | Data Space de test 21 | Test Data Space 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | AXONE 34 | 35 | 36 | 37 | test 38 | Dataset de test 39 | test Dataset 40 | Me 41 | 42 | A test Dataset. 43 | Un Dataset de test. 44 | 45 | 46 | 47 | 48 | 49 | 50 | 2023-03-28T00:00:00+00:00 51 | 52 | 2023-03-28T00:00:00+00:00 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/testdata/sample.ttl: -------------------------------------------------------------------------------- 1 | @prefix owl: . 2 | @prefix ns0: . 3 | @prefix xsd: . 4 | 5 | 6 | a owl:NamedIndividual, ; 7 | ns0:hasRegistrar . 8 | 9 | 10 | a owl:NamedIndividual, ; 11 | ns0:describes ; 12 | ns0:hasDescription "A test Data Space."@en, "Un Data Space de test."@fr ; 13 | ns0:hasPublisher "AXONE" ; 14 | ns0:hasTag "AXONE", "Test" ; 15 | ns0:hasTopic ; 16 | ns0:hasTitle "Data Space de test"@fr, "Test Data Space"@en . 17 | 18 | 19 | a owl:NamedIndividual, ns0:Dataset ; 20 | ns0:hasIdentifier ; 21 | ns0:providedBy ; 22 | ns0:belongsTo ; 23 | ns0:hasRegistrar . 24 | 25 | 26 | a owl:NamedIndividual, ; 27 | ns0:hasPublisher "AXONE" ; 28 | ns0:hasLicense ; 29 | ns0:hasFormat ; 30 | ns0:hasTag "test" ; 31 | ns0:hasTitle "Dataset de test"@fr, "test Dataset"@en ; 32 | ns0:hasCreator "Me" ; 33 | ns0:describes ; 34 | ns0:hasDescription "A test Dataset."@en, "Un Dataset de test."@fr ; 35 | ns0:hasTopic . 36 | 37 | 38 | a owl:NamedIndividual, ; 39 | ns0:createdBy ; 40 | ns0:lastModifiedBy ; 41 | ns0:updatedOn "2023-03-28T00:00:00+00:00"^^xsd:dateTime ; 42 | ns0:createdOn "2023-03-28T00:00:00+00:00"^^xsd:dateTime ; 43 | ns0:describes . 44 | -------------------------------------------------------------------------------- /contracts/axone-cognitarium/tests/e2e/features/insert.feature: -------------------------------------------------------------------------------- 1 | Feature: Cognitarium insertion 2 | 3 | Scenario: Inserting some rdf data 4 | This scenario demonstrates inserting some rdf data into the Cognitarium smart contract. 5 | 6 | Given a smart contract instantiated with message: 7 | """yaml 8 | limits: 9 | max_triple_count: '10000' 10 | max_byte_size: '2000000' 11 | max_triple_byte_size: '300' 12 | max_query_limit: 4 13 | max_query_variable_count: 5 14 | max_insert_data_byte_size: '10000' 15 | max_insert_data_triple_count: '100' 16 | """ 17 | When the smart contract is called with the following execute message: 18 | """yaml 19 | !insert_data 20 | format: turtle 21 | data: | 22 | @prefix ex: . 23 | @prefix xsd: . 24 | 25 | ex:Alice a ; 26 | ex:hasAge "30"^^xsd:integer ; 27 | ex:hasEmail "alice@example.com" . 28 | """ 29 | Then response is successful 30 | Then response attributes should be: 31 | | action | insert | 32 | | triple_count | 3 | 33 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | description = "The Smart Contract overseeing and managing the Dataverse in the AXONE ecosystem." 4 | edition = { workspace = true } 5 | homepage = { workspace = true } 6 | keywords = { workspace = true } 7 | license = { workspace = true } 8 | name = "axone-dataverse" 9 | repository = { workspace = true } 10 | rust-version = { workspace = true } 11 | version = { workspace = true } 12 | 13 | exclude = [ 14 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 15 | "contract.wasm", 16 | "hash.txt", 17 | ] 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [lib] 22 | crate-type = ["cdylib", "rlib"] 23 | 24 | [dependencies] 25 | axone-cognitarium.workspace = true 26 | axone-cognitarium-client.workspace = true 27 | axone-rdf.workspace = true 28 | base64 = "0.22.1" 29 | bech32 = "0.11.0" 30 | cosmwasm-schema.workspace = true 31 | cosmwasm-std.workspace = true 32 | cw-storage-plus.workspace = true 33 | cw-utils.workspace = true 34 | cw2.workspace = true 35 | itertools = "0.14.0" 36 | multibase = "0.9.1" 37 | rio_api.workspace = true 38 | ripemd = "0.1.3" 39 | serde.workspace = true 40 | sha2 = "0.10.9" 41 | thiserror.workspace = true 42 | unsigned-varint = "0.8.0" 43 | 44 | [dev-dependencies] 45 | base64 = "0.22.1" 46 | testing.workspace = true 47 | 48 | [features] 49 | # use library feature to disable all instantiate/execute/query exports 50 | library = [] 51 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.generate_schema] 2 | args = ["run", "--bin", "schema"] 3 | command = "cargo" 4 | 5 | [tasks.schema] 6 | dependencies = ["generate_schema"] 7 | script = ''' 8 | SCHEMA=$(find schema -type f -maxdepth 1 -name '*.json' -print0) 9 | TITLE=$(jq -r .contract_name $SCHEMA) 10 | jq --arg description "$(cat README.md)" '. + {description: $description}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 11 | jq --arg title $TITLE '. + {title: $title}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 12 | ''' 13 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/README.md: -------------------------------------------------------------------------------- 1 | # Dataverse 2 | 3 | ## Overview 4 | 5 | The `dataverse` smart contract is responsible for overseeing and managing the Dataverse. 6 | 7 | ## Dataverse 8 | 9 | The Dataverse is an ever-expanding universe that encompasses a wide range of digital resources. These include datasets, data processing algorithms, ML algorithm, storage resources, computational resources, identity management solutions, orchestration engines, oracles, and many other resources recorded on the blockchain. 10 | 11 | When the smart contract is instantiated, it creates a Dataverse instance. This instance is separated and isolated from any pre-existing ones, and as many dataverse instances as required can be created. 12 | 13 | ## Zones 14 | 15 | Zones within the Dataverse represent distinct areas or domains where specific governance rules and policies are applied. These Zones are conceptual frameworks created to manage and organize resources under a unified set of regulations and permissions. 16 | 17 | Each Zone is defined by its unique identity and set of governing rules, which dictate how resources within it can be accessed, used, and shared. This approach allows for granular control over different segments of the Dataverse, catering to various requirements and use cases. By managing these Zones, the dataverse smart contract ensures that resources are utilized in compliance with the defined policies and consents, thereby maintaining order and integrity within the Dataverse. 18 | 19 | ## Resources 20 | 21 | In the context of the Dataverse, Resources refer to a broad category of digital entities, which include Services and Digital Resources. 22 | 23 | - **Digital Resources**: This category extends to various digital entities such as datasets, algorithms, machine learning models, and other digital assets. Like Services, Digital Resources are identified by a URI in conjunction with the Service responsible for their provision. 24 | 25 | - **Services**: These are network-accessible functionalities like REST APIs, gRPC services, and other similar offerings. Each Service in the Dataverse is uniquely identified by its Uniform Resource Identifier (URI) and is associated with a specific Registrar responsible for its registration and management. 26 | 27 | ## Decentralized Identifiers (DID) 28 | 29 | Decentralized Identifiers (DID) are a foundational element in the Dataverse, serving as unique, persistent, and globally resolvable identifiers that are fully under the control of the DID subject, which could be an individual, organization, or a any kind of resource (dataset, 30 | algorithm, nft, ML algorithm). 31 | 32 | DIDs play a crucial role in the Dataverse by facilitating a trustable and interoperable identity mechanism. They enable the establishment of a verifiable and self-sovereign identity for resources, services, and entities within the ecosystem. 33 | 34 | ## Claims 35 | 36 | Claims in the Dataverse context are assertions or statements made about a Resource identified by a DID. 37 | 38 | Claims play a pivotal role in the governance framework of the Dataverse. By leveraging knowledge derived from verifiable credentials, the governances established by Zones can evaluate the fulfillment of specific rules and compliance. This evaluation is critical in ensuring that the resources within the Dataverse adhere to the established norms, policies, and requirements. 39 | 40 | Claims are submitted in the form of [Verifiable Presentations (VPs)](https://www.w3.org/TR/vc-data-model/#presentations), which are aggregations of one or more [Verifiable Credentials (VCs)](https://www.w3.org/TR/vc-data-model/#what-is-a-verifiable-credential). 41 | 42 | ## Dependencies 43 | 44 | Given its role and status, this smart contract serves as the primary access point for the AXONE protocol to manage all on-chain stored resources. To fulfill its tasks, the smart contract relies on other smart contracts within the AXONE ecosystem. Notably, it uses the `Cognitarium` smart contract for persisting the Dataverse representation in an ontological form and the `Law Stone` smart contract to establish governance rules. 45 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use axone_dataverse::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | execute: ExecuteMsg, 9 | query: QueryMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/credential/crypto.rs: -------------------------------------------------------------------------------- 1 | use crate::credential::error::VerificationError; 2 | use crate::credential::proof::ProofMaterial; 3 | use axone_rdf::normalize::Normalizer; 4 | use base64::prelude::BASE64_URL_SAFE_NO_PAD; 5 | use base64::Engine; 6 | use cosmwasm_std::DepsMut; 7 | use rio_api::model::Quad; 8 | use sha2::Digest; 9 | 10 | pub enum CanonicalizationAlg { 11 | Urdna2015, 12 | } 13 | 14 | pub enum DigestAlg { 15 | Sha256, 16 | } 17 | 18 | pub enum SignatureAlg { 19 | Ed25519, 20 | Secp256k1, 21 | } 22 | 23 | pub struct CryptoSuite { 24 | canon: CanonicalizationAlg, 25 | hash: DigestAlg, 26 | sign: SignatureAlg, 27 | } 28 | 29 | impl From<(CanonicalizationAlg, DigestAlg, SignatureAlg)> for CryptoSuite { 30 | fn from(value: (CanonicalizationAlg, DigestAlg, SignatureAlg)) -> Self { 31 | Self { 32 | canon: value.0, 33 | hash: value.1, 34 | sign: value.2, 35 | } 36 | } 37 | } 38 | 39 | impl CryptoSuite { 40 | pub fn verify_document( 41 | &self, 42 | deps: &'_ DepsMut<'_>, 43 | unsecured_doc: &[Quad<'_>], 44 | proof_opts: &[Quad<'_>], 45 | proof_material: ProofMaterial<'_>, 46 | pub_key: &[u8], 47 | ) -> Result<(), VerificationError> { 48 | let unsecured_doc_canon = self.canonicalize(unsecured_doc)?; 49 | let proof_opts_canon = self.canonicalize(proof_opts)?; 50 | 51 | let hash = [self.hash(proof_opts_canon), self.hash(unsecured_doc_canon)].concat(); 52 | 53 | match proof_material { 54 | ProofMaterial::Signature(v) => self.verify(deps, &hash, v, pub_key), 55 | ProofMaterial::Jws(jws) => { 56 | let (headers_b64, signature_b64) = Self::explode_jws(jws)?; 57 | let signature = BASE64_URL_SAFE_NO_PAD 58 | .decode(signature_b64) 59 | .map_err(|_| VerificationError::InvalidJws)?; 60 | 61 | let signing_input = [headers_b64, b".", &hash].concat(); 62 | let signing_input = match self.sign { 63 | SignatureAlg::Ed25519 => signing_input, 64 | SignatureAlg::Secp256k1 => { 65 | let mut hasher = sha2::Sha256::new(); 66 | hasher.update(signing_input); 67 | 68 | hasher.finalize().to_vec() 69 | } 70 | }; 71 | 72 | self.verify(deps, &signing_input, &signature, pub_key) 73 | } 74 | } 75 | } 76 | 77 | fn canonicalize(&self, unsecured_document: &[Quad<'_>]) -> Result { 78 | match self.canon { 79 | CanonicalizationAlg::Urdna2015 => { 80 | let mut normalizer = Normalizer::new(); 81 | normalizer 82 | .normalize(unsecured_document) 83 | .map_err(VerificationError::from) 84 | } 85 | } 86 | } 87 | 88 | fn hash(&self, transformed_document: String) -> Vec { 89 | match self.hash { 90 | DigestAlg::Sha256 => { 91 | let mut hasher = sha2::Sha256::new(); 92 | hasher.update(transformed_document); 93 | 94 | hasher.finalize().to_vec() 95 | } 96 | } 97 | } 98 | 99 | fn verify( 100 | &self, 101 | deps: &'_ DepsMut<'_>, 102 | message: &[u8], 103 | signature: &[u8], 104 | pub_key: &[u8], 105 | ) -> Result<(), VerificationError> { 106 | match match self.sign { 107 | SignatureAlg::Ed25519 => deps.api.ed25519_verify(message, signature, pub_key), 108 | SignatureAlg::Secp256k1 => deps.api.secp256k1_verify(message, signature, pub_key), 109 | } { 110 | Ok(true) => Ok(()), 111 | Ok(false) => Err(VerificationError::WrongSignature), 112 | Err(e) => Err(VerificationError::from(e)), 113 | } 114 | } 115 | 116 | fn explode_jws(jws: &str) -> Result<(&[u8], &[u8]), VerificationError> { 117 | let mut parts = jws.split('.'); 118 | Ok( 119 | match (parts.next(), parts.next(), parts.next(), parts.next()) { 120 | (Some(headers), Some(_), Some(sig), None) => (headers.as_bytes(), sig.as_bytes()), 121 | _ => Err(VerificationError::InvalidJws)?, 122 | }, 123 | ) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/credential/error.rs: -------------------------------------------------------------------------------- 1 | use axone_rdf::normalize::NormalizationError; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error, PartialEq)] 5 | pub enum InvalidCredentialError { 6 | #[error("Missing identifier")] 7 | MissingIdentifier, 8 | 9 | #[error("Missing issuer")] 10 | MissingIssuer, 11 | 12 | #[error("Missing issuance date")] 13 | MissingIssuanceDate, 14 | 15 | #[error("Invalid proof: {0}")] 16 | InvalidProof(#[from] InvalidProofError), 17 | 18 | #[error("Malformed: {0}")] 19 | Malformed(String), 20 | } 21 | 22 | #[derive(Debug, Error, PartialEq)] 23 | pub enum InvalidProofError { 24 | #[error("Missing proof type")] 25 | MissingProofType, 26 | 27 | #[error("Missing verification method")] 28 | MissingVerificationMethod, 29 | 30 | #[error("Missing created")] 31 | MissingCreated, 32 | 33 | #[error("Missing proof purpose")] 34 | MissingProofPurpose, 35 | 36 | #[error("Missing proof value")] 37 | MissingProofValue, 38 | 39 | #[error("Missing proof cryptosuite")] 40 | MissingProofCryptosuite, 41 | 42 | #[error("Malformed proof value: {0}")] 43 | MalformedProofValue(#[from] multibase::Error), 44 | 45 | #[error("Could not decode public key")] 46 | InvalidPubKey, 47 | 48 | #[error("Malformed: {0}")] 49 | Malformed(String), 50 | 51 | // Used internally only 52 | #[error("Unsupported proof type")] 53 | Unsupported, 54 | } 55 | 56 | #[derive(Debug, Error)] 57 | pub enum VerificationError { 58 | #[error("Couldn't canonicalize document: {0}")] 59 | RdfCanonError(#[from] NormalizationError), 60 | 61 | #[error("Couldn't verify signature: {0}")] 62 | SignatureError(#[from] cosmwasm_std::VerificationError), 63 | 64 | #[error("Invalid JWS")] 65 | InvalidJws, 66 | 67 | #[error("Signature mismatch")] 68 | WrongSignature, 69 | 70 | #[error("Couldn't find a suitable proof")] 71 | NoSuitableProof, 72 | } 73 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/credential/mod.rs: -------------------------------------------------------------------------------- 1 | mod crypto; 2 | pub mod error; 3 | mod proof; 4 | pub mod rdf_marker; 5 | pub mod vc; 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/credential/rdf_marker.rs: -------------------------------------------------------------------------------- 1 | use rio_api::model::{NamedNode, Term}; 2 | 3 | pub const RDF_TYPE: NamedNode<'_> = NamedNode { 4 | iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", 5 | }; 6 | pub const RDF_CREATED: NamedNode<'_> = NamedNode { 7 | iri: "http://purl.org/dc/terms/created", 8 | }; 9 | pub const RDF_DATE_TYPE: NamedNode<'_> = NamedNode { 10 | iri: "http://www.w3.org/2001/XMLSchema#dateTime", 11 | }; 12 | 13 | pub const RDF_UNSIGNED_INT: NamedNode<'_> = NamedNode { 14 | iri: "http://www.w3.org/2001/XMLSchema#unsignedInt", 15 | }; 16 | 17 | pub const RDF_UNSIGNED_LONG: NamedNode<'_> = NamedNode { 18 | iri: "http://www.w3.org/2001/XMLSchema#unsignedLong", 19 | }; 20 | 21 | pub const IRI_VC_TYPE: &str = "https://www.w3.org/2018/credentials#VerifiableCredential"; 22 | pub const VC_RDF_TYPE: Term<'_> = Term::NamedNode(NamedNode { iri: IRI_VC_TYPE }); 23 | pub const VC_RDF_ISSUER: NamedNode<'_> = NamedNode { 24 | iri: "https://www.w3.org/2018/credentials#issuer", 25 | }; 26 | pub const VC_RDF_ISSUANCE_DATE: NamedNode<'_> = NamedNode { 27 | iri: "https://www.w3.org/2018/credentials#issuanceDate", 28 | }; 29 | pub const VC_RDF_EXPIRATION_DATE: NamedNode<'_> = NamedNode { 30 | iri: "https://www.w3.org/2018/credentials#expirationDate", 31 | }; 32 | pub const VC_RDF_CREDENTIAL_SUBJECT: NamedNode<'_> = NamedNode { 33 | iri: "https://www.w3.org/2018/credentials#credentialSubject", 34 | }; 35 | pub const VC_RDF_CREDENTIAL_STATUS: NamedNode<'_> = NamedNode { 36 | iri: "https://www.w3.org/2018/credentials#credentialStatus", 37 | }; 38 | 39 | pub const VC_RDF_PROOF: NamedNode<'_> = NamedNode { 40 | iri: "https://w3id.org/security#proof", 41 | }; 42 | pub const PROOF_RDF_VERIFICATION_METHOD: NamedNode<'_> = NamedNode { 43 | iri: "https://w3id.org/security#verificationMethod", 44 | }; 45 | pub const PROOF_RDF_PROOF_PURPOSE: NamedNode<'_> = NamedNode { 46 | iri: "https://w3id.org/security#proofPurpose", 47 | }; 48 | pub const PROOF_RDF_PROOF_VALUE: NamedNode<'_> = NamedNode { 49 | iri: "https://w3id.org/security#proofValue", 50 | }; 51 | pub const PROOF_RDF_JWS: NamedNode<'_> = NamedNode { 52 | iri: "https://w3id.org/security#jws", 53 | }; 54 | pub const PROOF_RDF_PROOF_VALUE_TYPE: NamedNode<'_> = NamedNode { 55 | iri: "https://w3id.org/security#multibase", 56 | }; 57 | pub const PROOF_RDF_CRYPTOSUITE: NamedNode<'_> = NamedNode { 58 | iri: "https://w3id.org/security#cryptosuite", 59 | }; 60 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::credential::error::{InvalidCredentialError, VerificationError}; 2 | use axone_rdf::serde::NQuadsReadError; 3 | use cosmwasm_std::{Instantiate2AddressError, StdError}; 4 | use cw_utils::PaymentError; 5 | use thiserror::Error; 6 | 7 | #[derive(Debug, Error)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("{0}")] 13 | Instantiate2Address(#[from] Instantiate2AddressError), 14 | 15 | #[error("Couldn't parse RDF: '{0}'")] 16 | ParseRDF(#[from] NQuadsReadError), 17 | 18 | #[error("Invalid credential: '{0}'")] 19 | InvalidCredential(#[from] InvalidCredentialError), 20 | 21 | #[error("Credential verification failed: '{0}'")] 22 | CredentialVerification(#[from] VerificationError), 23 | 24 | #[error("Credential not supported: '{0}'")] 25 | UnsupportedCredential(String), 26 | 27 | #[error("Credential already exists: '{0}'")] 28 | CredentialAlreadyExists(String), 29 | 30 | #[error("An unexpected error occurred: {0}")] 31 | Unexpected(String), 32 | 33 | #[error("{0}")] 34 | Payment(#[from] PaymentError), 35 | } 36 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod credential; 3 | mod error; 4 | pub mod msg; 5 | mod registrar; 6 | pub mod state; 7 | mod testutil; 8 | 9 | pub use crate::error::ContractError; 10 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/registrar/credential.rs: -------------------------------------------------------------------------------- 1 | use crate::credential::rdf_marker::IRI_VC_TYPE; 2 | use crate::credential::vc::{Claim, VerifiableCredential}; 3 | use crate::ContractError; 4 | use cosmwasm_std::{Addr, Env, MessageInfo}; 5 | use itertools::Itertools; 6 | 7 | #[derive(Debug, PartialEq)] 8 | pub struct DataverseCredential<'a> { 9 | pub height: String, 10 | pub timestamp: String, 11 | pub tx_index: Option, 12 | pub sender: Addr, 13 | pub id: &'a str, 14 | pub issuer: &'a str, 15 | pub r#type: &'a str, 16 | pub valid_from: &'a str, 17 | pub valid_until: Option<&'a str>, 18 | pub claim: &'a Claim<'a>, 19 | } 20 | 21 | impl<'a> DataverseCredential<'a> { 22 | fn extract_vc_type(vc: &'a VerifiableCredential<'a>) -> Result<&'a str, ContractError> { 23 | vc.types 24 | .iter() 25 | .filter(|t| *t != &IRI_VC_TYPE) 26 | .exactly_one() 27 | .map_err(|_| { 28 | ContractError::UnsupportedCredential( 29 | "credential is expected to have exactly one type".to_string(), 30 | ) 31 | }) 32 | .copied() 33 | } 34 | 35 | fn extract_vc_claim(vc: &'a VerifiableCredential<'a>) -> Result<&'a Claim<'a>, ContractError> { 36 | vc.claims.iter().exactly_one().map_err(|_| { 37 | ContractError::UnsupportedCredential( 38 | "credential is expected to contain exactly one claim".to_string(), 39 | ) 40 | }) 41 | } 42 | } 43 | 44 | impl<'a> TryFrom<(Env, MessageInfo, &'a VerifiableCredential<'a>)> for DataverseCredential<'a> { 45 | type Error = ContractError; 46 | 47 | fn try_from( 48 | (env, info, vc): (Env, MessageInfo, &'a VerifiableCredential<'a>), 49 | ) -> Result { 50 | Ok(DataverseCredential { 51 | height: env.block.height.to_string(), 52 | timestamp: env.block.time.seconds().to_string(), 53 | tx_index: env.transaction.map(|tx| tx.index.to_string()), 54 | sender: info.sender, 55 | id: vc.id, 56 | issuer: vc.issuer, 57 | r#type: DataverseCredential::extract_vc_type(vc)?, 58 | valid_from: vc.issuance_date, 59 | valid_until: vc.expiration_date, 60 | claim: DataverseCredential::extract_vc_claim(vc)?, 61 | }) 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod test { 67 | use super::*; 68 | use crate::testutil::testutil; 69 | use axone_rdf::dataset::Dataset; 70 | use cosmwasm_std::testing::message_info; 71 | use rio_api::model::{Literal, NamedNode, Quad}; 72 | use testing::addr::{addr, SENDER}; 73 | use testing::mock::mock_env_addr; 74 | 75 | #[test] 76 | fn proper_from_verifiable() { 77 | let owned_quads = testutil::read_test_quads("vc-valid.nq"); 78 | let dataset = Dataset::from(owned_quads.as_slice()); 79 | let vc = VerifiableCredential::try_from(&dataset).unwrap(); 80 | let dc_res = 81 | DataverseCredential::try_from((mock_env_addr(), message_info(&addr(SENDER), &[]), &vc)); 82 | 83 | assert!(dc_res.is_ok()); 84 | assert_eq!(dc_res.unwrap(), DataverseCredential { 85 | height: "12345".to_string(), 86 | timestamp: "1571797419".to_string(), 87 | tx_index: Some("3".to_string()), 88 | sender: addr(SENDER), 89 | id: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/72cab400-5bd6-4eb4-8605-a5ee8c1a45c9", 90 | issuer: "did:key:zQ3shs7auhJSmVJpiUbQWco6bxxEhSqWnVEPvaBHBRvBKw6Q3", 91 | r#type: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/DigitalServiceDescriptionCredential", 92 | valid_from: "2024-01-22T00:00:00", 93 | valid_until: Some("2025-01-22T00:00:00"), 94 | claim: &Claim { 95 | id: "did:key:zQ3shhb4SvzBRLbBonsvKb3WX6WoDeKWHpsXXXMhAJETqXAfB", 96 | content: Dataset::new(vec![Quad { 97 | subject: NamedNode {iri: "did:key:zQ3shhb4SvzBRLbBonsvKb3WX6WoDeKWHpsXXXMhAJETqXAfB"}.into(), 98 | predicate: NamedNode {iri: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/hasCategory"}.into(), 99 | object: NamedNode{iri: "https://w3id.org/axone/ontology/vnext/thesaurus/digital-service-category/Storage"}.into(), 100 | graph_name: None, 101 | },Quad { 102 | subject: NamedNode {iri: "did:key:zQ3shhb4SvzBRLbBonsvKb3WX6WoDeKWHpsXXXMhAJETqXAfB"}.into(), 103 | predicate: NamedNode {iri: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/hasTag"}.into(), 104 | object: Literal::Simple {value: "Cloud"}.into(), 105 | graph_name: None, 106 | }]), 107 | }, 108 | }) 109 | } 110 | 111 | #[test] 112 | fn unsupported_from_verifiable() { 113 | let cases = vec![ 114 | ( 115 | "vc-unsupported-1.nq", 116 | "credential is expected to have exactly one type", 117 | ), 118 | ( 119 | "vc-unsupported-2.nq", 120 | "credential is expected to have exactly one type", 121 | ), 122 | ( 123 | "vc-unsupported-3.nq", 124 | "credential is expected to contain exactly one claim", 125 | ), 126 | ]; 127 | 128 | for case in cases { 129 | let owned_quads = testutil::read_test_quads(case.0); 130 | let dataset = Dataset::from(owned_quads.as_slice()); 131 | let vc = VerifiableCredential::try_from(&dataset).unwrap(); 132 | let dc_res = DataverseCredential::try_from(( 133 | mock_env_addr(), 134 | message_info(&addr(SENDER), &[]), 135 | &vc, 136 | )); 137 | 138 | assert!(dc_res.is_err()); 139 | if let ContractError::UnsupportedCredential(msg) = dc_res.err().unwrap() { 140 | assert_eq!(msg, case.1.to_string()); 141 | } else { 142 | assert!(false); 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/registrar/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod credential; 2 | mod rdf; 3 | pub mod registry; 4 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/registrar/registry.rs: -------------------------------------------------------------------------------- 1 | use crate::registrar::credential::DataverseCredential; 2 | use crate::state::DATAVERSE; 3 | use crate::ContractError; 4 | use axone_cognitarium::msg::DataFormat; 5 | use axone_cognitarium::parser::{ 6 | Node, SelectItem, SelectQuery, TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, 7 | WhereClause, IRI, 8 | }; 9 | use axone_cognitarium_client::CognitariumClient; 10 | use cosmwasm_std::{ensure, DepsMut, StdResult, Storage, WasmMsg}; 11 | 12 | /// ClaimRegistrar is the entity responsible to manage claims (i.e. submission and revocation) into 13 | /// the Dataverse, ensuring that any pre-condition criteria to an action is met, and any attached 14 | /// logic is properly executed. 15 | pub struct ClaimRegistrar { 16 | triplestore: CognitariumClient, 17 | } 18 | 19 | impl ClaimRegistrar { 20 | const RDF_DATA_FORMAT: DataFormat = DataFormat::NTriples; 21 | 22 | pub fn try_new(storage: &dyn Storage) -> StdResult { 23 | DATAVERSE.load(storage).map(|dataverse| Self { 24 | triplestore: CognitariumClient::new(dataverse.triplestore_address), 25 | }) 26 | } 27 | 28 | /// Checks if a credential exists in the triplestore by ID. 29 | /// Returns `true` if at least one triple is found, `false` otherwise. 30 | pub fn exists(&self, deps: &DepsMut<'_>, credential_id: &str) -> Result { 31 | let query = SelectQuery { 32 | prefixes: Vec::new(), 33 | limit: Some(1), 34 | select: vec![SelectItem::Variable("p".into())], 35 | r#where: WhereClause::Bgp { 36 | patterns: vec![TriplePattern { 37 | subject: VarOrNode::Node(Node::NamedNode(IRI::Full(credential_id.into()))), 38 | predicate: VarOrNamedNode::Variable("p".into()), 39 | object: VarOrNodeOrLiteral::Variable("o".into()), 40 | }], 41 | }, 42 | }; 43 | 44 | let response = self.triplestore.select(deps.querier, query)?; 45 | Ok(!response.results.bindings.is_empty()) 46 | } 47 | 48 | pub fn submit_claim( 49 | &self, 50 | deps: &DepsMut<'_>, 51 | credential: &DataverseCredential<'_>, 52 | ) -> Result { 53 | ensure!( 54 | !self.exists(deps, credential.id)?, 55 | ContractError::CredentialAlreadyExists(credential.id.to_string()) 56 | ); 57 | 58 | self.triplestore 59 | .insert_data( 60 | Some(Self::RDF_DATA_FORMAT), 61 | credential.serialize((&Self::RDF_DATA_FORMAT).into())?, 62 | ) 63 | .map_err(ContractError::from) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::Addr; 2 | use cw_storage_plus::Item; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | pub const DATAVERSE: Item = Item::new("dataverse"); 6 | 7 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 8 | pub struct Dataverse { 9 | pub name: String, 10 | pub triplestore_address: Addr, 11 | } 12 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/src/testutil.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | pub mod testutil { 3 | use axone_rdf::owned_model::OwnedQuad; 4 | use axone_rdf::serde::NQuadsReader; 5 | use std::env; 6 | use std::fs::File; 7 | use std::io::{BufReader, Read}; 8 | use std::path::Path; 9 | 10 | pub fn read_test_quads(file: &str) -> Vec { 11 | let raw_rdf = read_test_data(file); 12 | let buffer = BufReader::new(raw_rdf.as_slice()); 13 | let mut reader = NQuadsReader::new(buffer); 14 | reader.read_all().unwrap() 15 | } 16 | 17 | pub fn read_test_data(file: &str) -> Vec { 18 | let mut bytes: Vec = Vec::new(); 19 | 20 | File::open( 21 | Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()) 22 | .join("testdata") 23 | .join(file), 24 | ) 25 | .unwrap() 26 | .read_to_end(&mut bytes) 27 | .unwrap(); 28 | 29 | bytes 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-ed255192020-ok.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | _:b1 _:b0 . 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-ed255192020-options.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ . 2 | _:b1 . 3 | _:b1 . 4 | _:b1 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-invalid-pkey.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | _:b1 _:b0 . 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-malformed-value.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | _:b1 _:b0 . 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-malformed.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 "malformed" _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | _:b1 _:b0 . 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-missing-created.nq: -------------------------------------------------------------------------------- 1 | _:b1 _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 4 | _:b1 _:b0 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-missing-method.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-missing-purpose.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 4 | _:b1 _:b0 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-missing-type.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 4 | _:b1 _:b0 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-missing-value.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 _:b0 . 5 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/proof-unsupported.nq: -------------------------------------------------------------------------------- 1 | _:b1 "2023-11-29T10:07:56Z"^^ _:b0 . 2 | _:b1 _:b0 . 3 | _:b1 _:b0 . 4 | _:b1 "z5UT4w3v6uSJ3srR3ZFSZBbgjaMRyEUaaGdnZzEb2oc1YTskkpff9qYt2GiTDuU2wqEh3f99YvWubPuqVNWrn9hNx"^^ _:b0 . 5 | _:b1 _:b0 . 6 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-claim-hierarchy.nq: -------------------------------------------------------------------------------- 1 | . 2 | "Cloud" . 3 | . 4 | "nested value" . 5 | . 6 | . 7 | . 8 | "2024-01-22T00:00:00"^^ . 9 | "2025-01-22T00:00:00"^^ . 10 | . 11 | _:b0 . 12 | _:b1 "2024-02-01T17:46:53.676947Z"^^ _:b0 . 13 | _:b1 _:b0 . 14 | _:b1 _:b0 . 15 | _:b1 "z3WboEDRwsWokH8vQrveVWbg6fQnqhHfhrkGHT9tyG2GYgzQVZ9zFW6eK2ZNcnGhydqXWDwwTsZq29e7cHJkbnVkF"^^ _:b0 . 16 | _:b1 _:b0 . 17 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-di-ed-ok.nq: -------------------------------------------------------------------------------- 1 | . 2 | _:b0 . 3 | . 4 | "2023-05-01T06:09:10Z"^^ . 5 | . 6 | _:b1 "2024-02-07T13:24:37.636307Z"^^ _:b0 . 7 | _:b1 _:b0 . 8 | _:b1 "eddsa-2022" _:b0 . 9 | _:b1 _:b0 . 10 | _:b1 "z4JEgstWKYH2UxQK7VA6tYumr9XpEYUr66FdW4BtbbMBfxo3khn3ueHTAEL6c7EL9FU1pecZ471PkuRNSwHtNx7dz"^^ _:b0 . 11 | _:b1 _:b0 . 12 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-ecdsa-2019-ok.nq: -------------------------------------------------------------------------------- 1 | . 2 | _:b0 . 3 | . 4 | "2023-05-01T06:09:10Z"^^ . 5 | . 6 | _:b1 "2024-02-08T17:44:07.477489Z"^^ _:b0 . 7 | _:b1 _:b0 . 8 | _:b1 "eyJhbGciOiJFUzI1NksiLCJjcml0IjpbImI2NCJdLCJiNjQiOmZhbHNlfQ..Zhj537ApzBdQqfMKe2J9-I6CiNKzY6O2MZMrGwA2KZxMBSt81ExQJm2AKfX39OzeLn_gkP9Gmn_Bb_Yz2jFgtA" _:b0 . 9 | _:b1 _:b0 . 10 | _:b1 _:b0 . 11 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-eddsa-2018-ok.nq: -------------------------------------------------------------------------------- 1 | . 2 | _:b0 . 3 | . 4 | "2023-05-01T06:09:10Z"^^ . 5 | . 6 | _:b1 "2024-02-21T20:17:42.150598Z"^^ _:b0 . 7 | _:b1 _:b0 . 8 | _:b1 "eyJhbGciOiJFZERTQSIsImNyaXQiOlsiYjY0Il0sImI2NCI6ZmFsc2V9..5YzyqmQp1yPAea1WgKYzXOWTdYiDJO5iZs3bjxnSCzJYZS-ToIIqL4T47Ni7zZpc8S968vPKdCZcQzkoNnIIDw" _:b0 . 9 | _:b1 _:b0 . 10 | _:b1 _:b0 . 11 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-eddsa-2020-ok-unsecured-trusted.nq: -------------------------------------------------------------------------------- 1 | _:b2 . 2 | . 3 | . 4 | . 5 | "2026-02-16T00:00:00Z"^^ . 6 | "2024-02-16T00:00:00Z"^^ . 7 | . 8 | _:b2 "Bachelor of Science and Arts"^^ . 9 | _:b2 . 10 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-eddsa-2020-ok-unsecured.nq: -------------------------------------------------------------------------------- 1 | _:b2 . 2 | . 3 | . 4 | . 5 | "2026-02-16T00:00:00Z"^^ . 6 | "2024-02-16T00:00:00Z"^^ . 7 | . 8 | _:b2 "Bachelor of Science and Arts"^^ . 9 | _:b2 . 10 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-eddsa-2020-ok.nq: -------------------------------------------------------------------------------- 1 | _:b2 . 2 | . 3 | . 4 | _:b0 . 5 | . 6 | "2026-02-16T00:00:00Z"^^ . 7 | "2024-02-16T00:00:00Z"^^ . 8 | . 9 | _:b1 "2024-02-16T17:35:56.668169Z"^^ _:b0 . 10 | _:b1 _:b0 . 11 | _:b1 _:b0 . 12 | _:b1 "zUuTPsT5aKs53ciMY6qEj2dqZxK4XnLoZhX26amB9GMCMhfcTmLbtndcW5JS4gUqPkxGxsCmZCKuvkFnDgrGFrWD"^^ _:b0 . 13 | _:b1 _:b0 . 14 | _:b2 "Bachelor of Science and Arts"^^ . 15 | _:b2 . 16 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-unsupported-1.nq: -------------------------------------------------------------------------------- 1 | _:b2 . 2 | . 3 | _:b0 . 4 | . 5 | "2026-02-16T00:00:00Z"^^ . 6 | "2024-02-16T00:00:00Z"^^ . 7 | . 8 | _:b1 "2024-02-17T13:32:14.814613Z"^^ _:b0 . 9 | _:b1 _:b0 . 10 | _:b1 _:b0 . 11 | _:b1 "z5vGstniEMfyk5riV1UQvFXhXzdcbZ1978JFGdn1H2wTdp6qvxqDuw5xg8M33hjdZTG6zGuCbAhgCqf7R2CkhVRp5"^^ _:b0 . 12 | _:b1 _:b0 . 13 | _:b2 "Bachelor of Science and Arts"^^ . 14 | _:b2 . 15 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-unsupported-2.nq: -------------------------------------------------------------------------------- 1 | . 2 | "Cloud" . 3 | . 4 | . 5 | . 6 | . 7 | "2024-01-22T00:00:00"^^ . 8 | "2025-01-22T00:00:00"^^ . 9 | . 10 | _:b0 . 11 | _:b1 "2024-02-01T17:46:53.676947Z"^^ _:b0 . 12 | _:b1 _:b0 . 13 | _:b1 _:b0 . 14 | _:b1 "z3WboEDRwsWokH8vQrveVWbg6fQnqhHfhrkGHT9tyG2GYgzQVZ9zFW6eK2ZNcnGhydqXWDwwTsZq29e7cHJkbnVkF"^^ _:b0 . 15 | _:b1 _:b0 . 16 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-unsupported-3.nq: -------------------------------------------------------------------------------- 1 | . 2 | "Cloud" . 3 | "Cloud" . 4 | . 5 | . 6 | . 7 | . 8 | "2024-01-22T00:00:00"^^ . 9 | "2025-01-22T00:00:00"^^ . 10 | . 11 | _:b0 . 12 | _:b1 "2024-02-01T17:46:53.676947Z"^^ _:b0 . 13 | _:b1 _:b0 . 14 | _:b1 _:b0 . 15 | _:b1 "z3WboEDRwsWokH8vQrveVWbg6fQnqhHfhrkGHT9tyG2GYgzQVZ9zFW6eK2ZNcnGhydqXWDwwTsZq29e7cHJkbnVkF"^^ _:b0 . 16 | _:b1 _:b0 . 17 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-unsupported-4.nq: -------------------------------------------------------------------------------- 1 | . 2 | "Cloud" . 3 | "this shall not be allowed" . 4 | . 5 | . 6 | . 7 | "2024-01-22T00:00:00"^^ . 8 | "2025-01-22T00:00:00"^^ . 9 | . 10 | _:b0 . 11 | _:b1 "2024-02-01T17:46:53.676947Z"^^ _:b0 . 12 | _:b1 _:b0 . 13 | _:b1 _:b0 . 14 | _:b1 "z3WboEDRwsWokH8vQrveVWbg6fQnqhHfhrkGHT9tyG2GYgzQVZ9zFW6eK2ZNcnGhydqXWDwwTsZq29e7cHJkbnVkF"^^ _:b0 . 15 | _:b1 _:b0 . 16 | -------------------------------------------------------------------------------- /contracts/axone-dataverse/testdata/vc-valid.nq: -------------------------------------------------------------------------------- 1 | . 2 | "Cloud" . 3 | . 4 | . 5 | . 6 | "2024-01-22T00:00:00"^^ . 7 | "2025-01-22T00:00:00"^^ . 8 | . 9 | _:b0 . 10 | _:b1 "2024-02-01T17:46:53.676947Z"^^ _:b0 . 11 | _:b1 _:b0 . 12 | _:b1 _:b0 . 13 | _:b1 "z3WboEDRwsWokH8vQrveVWbg6fQnqhHfhrkGHT9tyG2GYgzQVZ9zFW6eK2ZNcnGhydqXWDwwTsZq29e7cHJkbnVkF"^^ _:b0 . 14 | _:b1 _:b0 . 15 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | description = "The Smart Contract providing Governance as a Service." 4 | edition = { workspace = true } 5 | homepage = { workspace = true } 6 | keywords = { workspace = true } 7 | license = { workspace = true } 8 | name = "axone-law-stone" 9 | repository = { workspace = true } 10 | rust-version = { workspace = true } 11 | version = { workspace = true } 12 | 13 | exclude = [ 14 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 15 | "contract.wasm", 16 | "hash.txt", 17 | ] 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [lib] 22 | crate-type = ["cdylib", "rlib"] 23 | 24 | [dependencies] 25 | axone-logic-bindings.workspace = true 26 | axone-objectarium.workspace = true 27 | axone-objectarium-client.workspace = true 28 | axone-wasm.workspace = true 29 | cosmwasm-schema.workspace = true 30 | cosmwasm-std.workspace = true 31 | cw-storage-plus.workspace = true 32 | cw-utils.workspace = true 33 | cw2.workspace = true 34 | itertools = "0.14.0" 35 | serde.workspace = true 36 | thiserror.workspace = true 37 | 38 | [dev-dependencies] 39 | testing.workspace = true 40 | url = "2.5.4" 41 | 42 | [features] 43 | # use library feature to disable all instantiate/execute/query exports 44 | library = [] 45 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.generate_schema] 2 | args = ["run", "--bin", "schema"] 3 | command = "cargo" 4 | 5 | [tasks.schema] 6 | dependencies = ["generate_schema"] 7 | script = ''' 8 | SCHEMA=$(find schema -type f -maxdepth 1 -name '*.json' -print0) 9 | TITLE=$(jq -r .contract_name $SCHEMA) 10 | jq --arg description "$(cat README.md)" '. + {description: $description}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 11 | jq --arg title $TITLE '. + {title: $title}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 12 | ''' 13 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/README.md: -------------------------------------------------------------------------------- 1 | # Law Stone 2 | 3 | ## Overview 4 | 5 | The `axone-law-stone` smart contract aims to provide GaaS (i.e. Governance as a Service) in any [Cosmos blockchains](https://cosmos.network/) using the [CosmWasm](https://cosmwasm.com/) framework and the [Logic](https://docs.axone.xyz/modules/next/logic) AXONE module. 6 | 7 | This contract is built around a Prolog program describing the law by rules and facts. The law stone is immutable, this means it can only be questioned, there are no update mechanisms. 8 | 9 | The `axone-law-stone` responsibility is to guarantee the availability of its rules in order to question them, but not to ensure the rules application. 10 | 11 | To ensure reliability over time, the associated Prolog program is stored and pinned in a `axone-objectarium` contract. Moreover, all the eventual loaded files must be stored in a `axone-objectarium` contract as well, allowing the contract to pin them. 12 | 13 | To be able to free the underlying resources (i.e. objects in `axone-objectarium`) if not used anymore, the contract admin can break the stone. 14 | 15 | ➡️ Checkout the [examples](https://github.com/axone-protocol/contracts/tree/main/contracts/axone-law-stone/examples/) for usage information. 16 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/examples/multiple-sources/README.md: -------------------------------------------------------------------------------- 1 | # Multiple source 2 | 3 | When executed by the logic module, a Prolog program can load other programs through the `consult(File).` predicate. This example aims to illustrate this case, when the law is composed of multiple Prolog sources. 4 | 5 | ## The Program 6 | 7 | We'll reuse the exact same story as the [single-source](../single-source/README.md) example, we'll just split the program in two: 8 | 9 | - `template.pl`: Contains the governance rules predicates, which can be customized by defining configuration predicates; 10 | - `gov.pl`: Load `template.pl` and define configuration predicates. 11 | 12 | ⚠️ A special attention must be brought to the template loading with the `consult(File).` predicate. 13 | 14 | The Logic module expects the `File` variable to be an URI so it can resolve its content. Through the `cosmwasm` prefix it can loads data from any smart contract query, we'll configure the URI to perform a `axone-objectarium` `ObjectData` query in order to load the `template.pl`. 15 | 16 | The URI has the following form: 17 | 18 | ```bash 19 | cosmwasm:{contract_name}:{contract_address}?query={contract_query} 20 | ``` 21 | 22 | Where: 23 | 24 | - `{contract_name}`: Only informative, represents the corresponding smart contract name or type (e.g. `axone-objectarium`); 25 | - `{contract_address}`: The smart contract to query, concerning the `axone-law-stone` it must be a `axone-objectarium` contract; 26 | - `{contract_query}`: The JSON query to perform on the targeted smart contract, URL encoded. In our case an `ObjectData` query, for example: `%7B%22object_data%22%3A%7B%22id%22%3A%22b118d79b4a368028b34d564448e5f1082e098613434370f3c15d6a2bf9979dfc%22%7D%7D`; 27 | 28 | ## Instantiate 29 | 30 | First the `template.pl` program must be stored on a `axone-objectarium` and the `gov.pl` updated with the right URI in the `consult(File).` predicate, the URI should be in the form: 31 | 32 | ```bash 33 | cosmwasm:axone-objectarium:${STORAGE_ADDRESS}?query=%7B%22object_data%22%3A%7B%22id%22%3A%22b118d79b4a368028b34d564448e5f1082e098613434370f3c15d6a2bf9979dfc%22%7D%7D 34 | ``` 35 | 36 | The instantiate will take as parameters the base64 encoded program and the address of a `axone-objectarium` contract, on which the program will be stored and pinned, the `template.pl` object will also be pinned to ensure all the needed resources stays available: 37 | 38 | ```bash 39 | axoned tx wasm instantiate $CODE_ID \ 40 | --label "multiple-source" \ 41 | --from $ADDR \ 42 | --admin $ADMIN_ADDR \ 43 | --gas 1000000 \ 44 | "{\"program\":\"$(cat gov.pl | base64)\", \"storage_address\": \"$STORAGE_ADDR\"}" 45 | ``` 46 | 47 | You can retrieve the new `axone-law-stone` smart contract address in the `_contract_address` instantiate attribute of the transaction. 48 | 49 | ## Query 50 | 51 | By using the `Ask` query we can provide Prolog predicates to be evaluated againsts the underlying programs: 52 | 53 | ```bash 54 | axoned query wasm contract-state smart $CONTRACT_ADDR \ 55 | "{\"ask\": {\"query\": \"can('change_governance', 'did:example:axone1p8u47en82gmzfm259y6z93r9qe63l25d858vqu').\"}}" 56 | ``` 57 | 58 | ## Break 59 | 60 | Only the smart contract admin can break the stone, if any. 61 | 62 | The program stored in the `axone-objectarium` smart contract will be removed, or at least un-pinned. And the `template.pl` object will be un pinned. 63 | 64 | By breaking the stone, you will not be able to query it anymore. 65 | 66 | ```bash 67 | axoned tx wasm execute $CONTRACT_ADDR \ 68 | --from $ADDR \ 69 | --gas 1000000 \ 70 | '"break_stone"' 71 | ``` 72 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/examples/multiple-sources/gov.pl: -------------------------------------------------------------------------------- 1 | :- consult('cosmwasm:axone-objectarium:${STORAGE_ADDRESS}?query=%7B%22object_data%22%3A%7B%22id%22%3A%22b118d79b4a368028b34d564448e5f1082e098613434370f3c15d6a2bf9979dfc%22%7D%7D'). 2 | 3 | admin_addr('axone1p8u47en82gmzfm259y6z93r9qe63l25d858vqu'). 4 | 5 | allow_denom('uaxone'). 6 | allow_did_method('example'). 7 | allow_addr(Addr) :- bech32_address(-('axone', _), Addr). 8 | 9 | min_exec_workflow_amount(1000000). 10 | min_create_dataset_amount(10000). 11 | min_create_service_amount(100000). 12 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/examples/multiple-sources/template.pl: -------------------------------------------------------------------------------- 1 | valid_did(DID, Addr) :- 2 | did_components(DID, did_components(Method, Addr, _, _, _)), 3 | allow_did_method(Method), 4 | allow_addr(Addr). 5 | 6 | min_amount(exec_workflow, MinAmount) :- 7 | min_exec_workflow_amount(MinAmount). 8 | 9 | min_amount(create_dataset, MinAmount) :- 10 | min_create_dataset_amount(MinAmount). 11 | 12 | min_amount(create_service, MinAmount) :- 13 | min_create_service_amount(MinAmount). 14 | 15 | has_sufficient_balance(Addr, MinAmount) :- 16 | bank_spendable_balances(Addr, Balances), 17 | member(Denom-Amount, Balances), 18 | allow_denom(Denom), 19 | Amount @>= MinAmount. 20 | 21 | can(change_governance, DID) :- 22 | valid_did(DID, Addr), 23 | admin_addr(Addr). 24 | 25 | can(Action, DID) :- 26 | valid_did(DID, Addr), 27 | min_amount(Action, MinAmount), 28 | has_sufficient_balance(Addr, MinAmount). 29 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/examples/single-source/README.md: -------------------------------------------------------------------------------- 1 | # Single source 2 | 3 | This example aims to illustrate the most simple case of the `axone-law-stone`: The law is composed of only one Prolog source program. 4 | 5 | ## The Program 6 | 7 | The spirit here is to provide a `axone-law-stone` smart contract instance providing rules similar in form to Dataspace governance rules. 8 | 9 | You'll find in the [gov.pl](gov.pl) Prolog program some predicates defining the rules allowing to perform some typical Dataspaces actions. 10 | 11 | The `can(Action, DID)` predicate will allow or not an action for a `did` (i.e. Decentralized Identifier), a `did` being expected to have the form: `did:example:${AXONE_ADDRESS}`. We can describe the action rules as follows: 12 | 13 | - `change_governance`: Only the did admin can do it: `did:example:axone1p8u47en82gmzfm259y6z93r9qe63l25d858vqu`; 14 | - `exec_workflow`: Only a valid DID having a minimum spendable of `1000000uaxone`; 15 | - `create_dataset` Only a valid DID having a minimum spendable of `10000uaxone`; 16 | - `create_service` Only a valid DID having a minimum spendable of `100000uaxone`; 17 | 18 | ## Instantiate 19 | 20 | The instantiate will take as parameters the base64 encoded program and the address of a `axone-objectarium` contract, on which the program will be stored and pinned to prevent its removal and thus ensure its availability: 21 | 22 | ```bash 23 | axoned tx wasm instantiate $CODE_ID \ 24 | --label "single-source" \ 25 | --from $ADDR \ 26 | --admin $ADMIN_ADDR \ 27 | --gas 1000000 \ 28 | "{\"program\":\"$(cat gov.pl | base64)\", \"storage_address\": \"$STORAGE_ADDR\"}" 29 | ``` 30 | 31 | You can retrieve the new `axone-law-stone` smart contract address in the `_contract_address` instantiate attribute of the transaction. 32 | 33 | ## Query 34 | 35 | By using the `Ask` query we can provide Prolog predicates to be evaluated againsts the underlying program: 36 | 37 | ```bash 38 | axoned query wasm contract-state smart $CONTRACT_ADDR \ 39 | "{\"ask\": {\"query\": \"can('change_governance', 'did:example:axone1p8u47en82gmzfm259y6z93r9qe63l25d858vqu').\"}}" 40 | ``` 41 | 42 | ## Break 43 | 44 | Only the smart contract admin can break the stone, if any. 45 | 46 | The program stored in the `axone-objectarium` smart contract will be removed, or at least un-pinned. 47 | 48 | By breaking the stone, you will not be able to query it anymore. 49 | 50 | ```bash 51 | axoned tx wasm execute $CONTRACT_ADDR \ 52 | --from $ADDR \ 53 | --gas 1000000 \ 54 | '"break_stone"' 55 | ``` 56 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/examples/single-source/gov.pl: -------------------------------------------------------------------------------- 1 | admin_addr('axone1p8u47en82gmzfm259y6z93r9qe63l25d858vqu'). 2 | 3 | allow_denom('uaxone'). 4 | allow_did_method('example'). 5 | allow_addr(Addr) :- bech32_address(-('axone', _), Addr). 6 | 7 | valid_did(DID, Addr) :- 8 | did_components(DID, did_components(Method, Addr, _, _, _)), 9 | allow_did_method(Method), 10 | allow_addr(Addr). 11 | 12 | min_exec_workflow_amount(1000000). 13 | min_create_dataset_amount(10000). 14 | min_create_service_amount(100000). 15 | 16 | min_amount(exec_workflow, MinAmount) :- 17 | min_exec_workflow_amount(MinAmount). 18 | 19 | min_amount(create_dataset, MinAmount) :- 20 | min_create_dataset_amount(MinAmount). 21 | 22 | min_amount(create_service, MinAmount) :- 23 | min_create_service_amount(MinAmount). 24 | 25 | has_sufficient_balance(Addr, MinAmount) :- 26 | bank_spendable_balances(Addr, Balances), 27 | member(Denom-Amount, Balances), 28 | allow_denom(Denom), 29 | Amount @>= MinAmount. 30 | 31 | can(change_governance, DID) :- 32 | valid_did(DID, Addr), 33 | admin_addr(Addr). 34 | 35 | can(Action, DID) :- 36 | valid_did(DID, Addr), 37 | min_amount(Action, MinAmount), 38 | has_sufficient_balance(Addr, MinAmount). 39 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use axone_law_stone::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | execute: ExecuteMsg, 9 | query: QueryMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/src/error.rs: -------------------------------------------------------------------------------- 1 | use axone_logic_bindings::error::TermParseError; 2 | use axone_wasm::error::CosmwasmUriError; 3 | use cosmwasm_std::StdError; 4 | use cw_utils::{ParseReplyError, PaymentError}; 5 | use thiserror::Error; 6 | 7 | #[derive(Debug, Error, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("{0}")] 13 | ParseReplyError(#[from] ParseReplyError), 14 | 15 | #[error("An unknown reply ID was received.")] 16 | UnknownReplyID, 17 | 18 | #[error("Cannot parse cosmwasm uri: {0}")] 19 | ParseCosmwasmUri(CosmwasmUriError), 20 | 21 | #[error("Cannot extract data from logic ask response: {0}")] 22 | LogicAskResponse(LogicAskResponseError), 23 | 24 | #[error("Only the contract admin can perform this operation.")] 25 | Unauthorized, 26 | 27 | #[error("{0}")] 28 | Payment(#[from] PaymentError), 29 | } 30 | 31 | #[derive(Debug, Eq, Error, PartialEq)] 32 | pub enum LogicAskResponseError { 33 | #[error("Could not parse term: {0}")] 34 | Parse(TermParseError), 35 | 36 | #[error("Substitution error: {0}")] 37 | Substitution(String), 38 | 39 | #[error("Unexpected response: {0}")] 40 | Unexpected(String), 41 | 42 | #[error("Invalid parsed term format.")] 43 | UnexpectedTerm, 44 | } 45 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | mod helper; 4 | pub mod msg; 5 | pub mod state; 6 | 7 | pub use crate::error::ContractError; 8 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/src/msg.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | use axone_logic_bindings::AskResponse; 3 | use cosmwasm_schema::{cw_serde, QueryResponses}; 4 | use cosmwasm_std::Binary; 5 | 6 | /// Instantiate message 7 | #[cw_serde] 8 | pub struct InstantiateMsg { 9 | /// The Prolog program carrying law rules and facts. 10 | pub program: Binary, 11 | 12 | /// The `axone-objectarium` contract address on which to store the law program. 13 | pub storage_address: String, 14 | } 15 | 16 | /// Execute messages 17 | #[cw_serde] 18 | pub enum ExecuteMsg { 19 | /// # BreakStone 20 | /// Break the stone making this contract unusable, by clearing all the related resources: 21 | /// - Unpin all the pinned objects on `axone-objectarium` contracts, if any. 22 | /// - Forget the main program (i.e. or at least unpin it). 23 | /// 24 | /// Only the creator address (the address that instantiated the contract) is authorized to invoke 25 | /// this message. 26 | /// If already broken, this is a no-op. 27 | BreakStone {}, 28 | } 29 | 30 | /// Query messages 31 | #[cw_serde] 32 | #[derive(QueryResponses)] 33 | pub enum QueryMsg { 34 | /// # Ask 35 | /// Submits a Prolog query string to the `Logic` module, evaluating it against the 36 | /// law program associated with this contract. 37 | /// 38 | /// If the law stone is broken the query returns a response with the error `error(system_error(broken_law_stone),root)` 39 | /// set in the `answer` field. 40 | #[returns(AskResponse)] 41 | Ask { query: String }, 42 | 43 | /// # Program 44 | /// Retrieves the location metadata of the law program bound to this contract. 45 | /// 46 | /// This includes the contract address of the `objectarium` and the program object ID, 47 | /// where the law program's code can be accessed. 48 | #[returns(ProgramResponse)] 49 | Program {}, 50 | 51 | /// # ProgramCode 52 | /// Fetches the raw code of the law program tied to this contract. 53 | /// 54 | /// If the law stone is broken, the query may fail if the program is no longer available in the 55 | /// `Objectarium`. 56 | #[returns(Binary)] 57 | ProgramCode {}, 58 | } 59 | 60 | /// # ProgramResponse 61 | /// ProgramResponse carry elements to locate the program in a `axone-objectarium` contract. 62 | #[cw_serde] 63 | pub struct ProgramResponse { 64 | /// The program object id in the `axone-objectarium` contract. 65 | pub object_id: String, 66 | 67 | /// The `axone-objectarium` contract address on which the law program is stored. 68 | pub storage_address: String, 69 | } 70 | -------------------------------------------------------------------------------- /contracts/axone-law-stone/src/state.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::msg::ProgramResponse; 4 | use axone_objectarium_client::ObjectRef; 5 | use cw_storage_plus::{Item, Map}; 6 | 7 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 8 | pub struct LawStone { 9 | pub broken: bool, 10 | pub law: ObjectRef, 11 | } 12 | 13 | impl From for ProgramResponse { 14 | fn from(value: LawStone) -> ProgramResponse { 15 | ProgramResponse { 16 | object_id: value.law.object_id, 17 | storage_address: value.law.storage_address, 18 | } 19 | } 20 | } 21 | 22 | pub const PROGRAM: Item = Item::new("program"); 23 | 24 | pub const DEPENDENCIES: Map<&str, ObjectRef> = Map::new("dependencies"); 25 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | description = "A Smart Contract which enables the storage of arbitrary unstructured Objects." 4 | edition = { workspace = true } 5 | homepage = { workspace = true } 6 | keywords = { workspace = true } 7 | license = { workspace = true } 8 | name = "axone-objectarium" 9 | repository = { workspace = true } 10 | rust-version = { workspace = true } 11 | version = { workspace = true } 12 | 13 | exclude = [ 14 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 15 | "contract.wasm", 16 | "hash.txt", 17 | ] 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [lib] 22 | crate-type = ["cdylib", "rlib"] 23 | 24 | [dependencies] 25 | base16ct = { version = "0.2.0", features = ["alloc"] } 26 | bs58 = "0.5.1" 27 | cosmwasm-schema.workspace = true 28 | cosmwasm-std.workspace = true 29 | cw-storage-plus.workspace = true 30 | cw-utils.workspace = true 31 | cw2.workspace = true 32 | derive_builder = "0.20.2" 33 | either = "1.15.0" 34 | enum-iterator = "2.1.0" 35 | lzma-rs = "0.3.0" 36 | md-5 = "0.10.6" 37 | schemars.workspace = true 38 | serde.workspace = true 39 | sha2 = "0.10.9" 40 | snap = "1" 41 | thiserror.workspace = true 42 | 43 | [dev-dependencies] 44 | base64 = "0.22.1" 45 | testing.workspace = true 46 | 47 | [features] 48 | # use library feature to disable all instantiate/execute/query exports 49 | library = [] 50 | 51 | [package.metadata.cargo-machete] 52 | ignored = ["md5"] 53 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.generate_schema] 2 | args = ["run", "--bin", "schema"] 3 | command = "cargo" 4 | 5 | [tasks.schema] 6 | dependencies = ["generate_schema"] 7 | script = ''' 8 | SCHEMA=$(find schema -type f -maxdepth 1 -name '*.json' -print0) 9 | TITLE=$(jq -r .contract_name $SCHEMA) 10 | jq --arg description "$(cat README.md)" '. + {description: $description}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 11 | jq --arg title $TITLE '. + {title: $title}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 12 | ''' 13 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use axone_objectarium::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | execute: ExecuteMsg, 9 | query: QueryMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/compress.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use enum_iterator::Sequence; 4 | use lzma_rs; 5 | use schemars::JsonSchema; 6 | use serde::{Deserialize, Serialize}; 7 | use snap; 8 | use thiserror::Error; 9 | 10 | /// CompressionAlgorithm is an enumeration that defines the different compression algorithms 11 | /// supported for compressing the content of objects. 12 | #[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Sequence, Serialize)] 13 | pub enum CompressionAlgorithm { 14 | /// Represents the "No compression" algorithm. 15 | Passthrough, 16 | /// Represents the Snappy algorithm. 17 | Snappy, 18 | /// Represents the LZMA algorithm. 19 | Lzma, 20 | } 21 | 22 | impl CompressionAlgorithm { 23 | /// compress returns the compressed data using the given algorithm. 24 | pub fn compress(&self, data: &[u8]) -> Result, CompressionError> { 25 | let compressor = match self { 26 | CompressionAlgorithm::Passthrough => passthrough, 27 | CompressionAlgorithm::Snappy => snappy_compress, 28 | CompressionAlgorithm::Lzma => lzma_compress, 29 | }; 30 | compressor(data) 31 | } 32 | 33 | /// decompress returns the decompressed data using the given algorithm. 34 | /// The data must be compressed using the same algorithm. 35 | pub fn decompress(&self, data: &[u8]) -> Result, CompressionError> { 36 | let decompressor = match self { 37 | CompressionAlgorithm::Passthrough => passthrough, 38 | CompressionAlgorithm::Snappy => snappy_decompress, 39 | CompressionAlgorithm::Lzma => lzma_decompress, 40 | }; 41 | decompressor(data) 42 | } 43 | } 44 | 45 | #[derive(Debug, Eq, Error, PartialEq)] 46 | pub enum CompressionError { 47 | #[error("{0}")] 48 | Error(String), 49 | } 50 | 51 | impl From for CompressionError { 52 | fn from(err: io::Error) -> Self { 53 | CompressionError::Error(err.to_string()) 54 | } 55 | } 56 | 57 | impl From for CompressionError { 58 | fn from(err: lzma_rs::error::Error) -> Self { 59 | CompressionError::Error(err.to_string()) 60 | } 61 | } 62 | 63 | /// pass_through returns the data as is. 64 | #[inline] 65 | #[allow(clippy::unnecessary_wraps)] 66 | fn passthrough(data: &[u8]) -> Result, CompressionError> { 67 | Ok(data.to_vec()) 68 | } 69 | 70 | // snappy_compress returns the Snappy compressed data. 71 | #[inline] 72 | fn snappy_compress(data: &[u8]) -> Result, CompressionError> { 73 | let mut reader = io::Cursor::new(data); 74 | let mut writer = Vec::new(); 75 | { 76 | let mut snappy_writer = snap::write::FrameEncoder::new(&mut writer); 77 | io::copy(&mut reader, &mut snappy_writer)?; 78 | } 79 | Ok(writer) 80 | } 81 | 82 | // snappy_decompress returns the Snappy decompressed data. 83 | #[inline] 84 | fn snappy_decompress(data: &[u8]) -> Result, CompressionError> { 85 | let reader = io::Cursor::new(data); 86 | let mut snappy_reader = snap::read::FrameDecoder::new(reader); 87 | let mut writer = Vec::new(); 88 | io::copy(&mut snappy_reader, &mut writer)?; 89 | Ok(writer) 90 | } 91 | 92 | // lzma_compress returns the LZMA compressed data. 93 | #[inline] 94 | fn lzma_compress(data: &[u8]) -> Result, CompressionError> { 95 | let mut reader = io::Cursor::new(data); 96 | let mut writer = Vec::new(); 97 | lzma_rs::lzma_compress(&mut reader, &mut writer)?; 98 | Ok(writer) 99 | } 100 | 101 | // lzma_decompress returns the LZMA decompressed data. 102 | #[inline] 103 | fn lzma_decompress(data: &[u8]) -> Result, CompressionError> { 104 | let mut reader = io::Cursor::new(data); 105 | let mut writer = Vec::new(); 106 | lzma_rs::lzma_decompress(&mut reader, &mut writer)?; 107 | Ok(writer) 108 | } 109 | 110 | #[cfg(test)] 111 | mod tests { 112 | use super::*; 113 | 114 | #[test] 115 | fn test_from_io_decompress_error() { 116 | let cases = vec![ 117 | ( 118 | std::io::Error::new( 119 | std::io::ErrorKind::InvalidData, 120 | "the expected decompressed size differs, actual 998, expected 1000", 121 | ), 122 | CompressionError::Error( 123 | "the expected decompressed size differs, actual 998, expected 1000".to_string(), 124 | ), 125 | ), 126 | ( 127 | std::io::Error::new( 128 | std::io::ErrorKind::InvalidData, 129 | lzma_rs::error::Error::IoError(std::io::Error::new( 130 | std::io::ErrorKind::InvalidData, 131 | "the expected decompressed size differs, actual 998, expected 1000", 132 | )), 133 | ), 134 | CompressionError::Error( 135 | "io error: the expected decompressed size differs, actual 998, expected 1000" 136 | .to_string(), 137 | ), 138 | ), 139 | ]; 140 | 141 | for (error, expected_error) in cases { 142 | let compression_err = CompressionError::from(error); 143 | 144 | assert_eq!(compression_err, expected_error); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/crypto.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{StdError, StdResult}; 2 | use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; 3 | use md5; 4 | use schemars::JsonSchema; 5 | use serde::{Deserialize, Serialize}; 6 | use sha2; 7 | use sha2::Digest; 8 | use std::any::type_name; 9 | use std::fmt; 10 | 11 | /// HashAlgorithm is the type of the hash algorithm. 12 | pub enum HashAlgorithm { 13 | /// Represents the MD5 algorithm. 14 | MD5, 15 | /// Represents the SHA-224 algorithm. 16 | Sha224, 17 | /// Represents the SHA-256 algorithm. 18 | Sha256, 19 | /// Represents the SHA-384 algorithm. 20 | Sha384, 21 | /// Represents the SHA-512 algorithm. 22 | Sha512, 23 | } 24 | 25 | impl HashAlgorithm { 26 | /// hash returns the hash of the given data using the given algorithm. 27 | pub fn hash_fn(&self) -> HashFn { 28 | match self { 29 | HashAlgorithm::MD5 => md5_hash, 30 | HashAlgorithm::Sha224 => sha224_hash, 31 | HashAlgorithm::Sha256 => sha256_hash, 32 | HashAlgorithm::Sha384 => sha384_hash, 33 | HashAlgorithm::Sha512 => sha512_hash, 34 | } 35 | } 36 | } 37 | 38 | /// Hash represent a Object hash as binary value. 39 | #[derive( 40 | Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, JsonSchema, 41 | )] 42 | pub struct Hash(Vec); 43 | 44 | /// HashFn is the type of the function used to hash data. 45 | pub type HashFn = fn(&Vec) -> Hash; 46 | 47 | /// hash returns the hash of the given data using the given algorithm. 48 | pub fn hash<'a>(algorithm: &'a HashAlgorithm, data: &'a Vec) -> Hash { 49 | algorithm.hash_fn()(data) 50 | } 51 | 52 | /// md5_hash returns the MD5 hash of the given data. 53 | fn md5_hash(data: &Vec) -> Hash { 54 | md5::Md5::digest(data).to_vec().into() 55 | } 56 | 57 | /// sha224_hash returns the SHA-224 hash of the given data. 58 | fn sha224_hash(data: &Vec) -> Hash { 59 | sha2::Sha224::digest(data).to_vec().into() 60 | } 61 | 62 | /// sha256_hash returns the SHA-256 hash of the given data. 63 | fn sha256_hash(data: &Vec) -> Hash { 64 | sha2::Sha256::digest(data).to_vec().into() 65 | } 66 | 67 | /// sha384_hash returns the SHA-384 hash of the given data. 68 | fn sha384_hash(data: &Vec) -> Hash { 69 | sha2::Sha384::digest(data).to_vec().into() 70 | } 71 | 72 | /// sha512_hash returns the SHA-512 hash of the given data. 73 | fn sha512_hash(data: &Vec) -> Hash { 74 | sha2::Sha512::digest(data).to_vec().into() 75 | } 76 | 77 | impl TryFrom for Hash { 78 | type Error = StdError; 79 | 80 | fn try_from(s: String) -> StdResult { 81 | base16ct::lower::decode_vec(s) 82 | .map_err(|e| StdError::parse_err(type_name::>(), e.to_string())) 83 | .map(Hash) 84 | } 85 | } 86 | 87 | // Allows for a (user-friendly) string representation of Hash as a lower Base16 (hex) encoding. 88 | impl fmt::Display for Hash { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 90 | let hex_string = base16ct::lower::encode_string(&self.0); 91 | write!(f, "{}", hex_string) 92 | } 93 | } 94 | 95 | impl From> for Hash { 96 | fn from(hash: Vec) -> Self { 97 | Hash(hash) 98 | } 99 | } 100 | 101 | impl From for Vec { 102 | fn from(hash: Hash) -> Self { 103 | hash.0 104 | } 105 | } 106 | 107 | impl From<&Hash> for Vec { 108 | fn from(hash: &Hash) -> Self { 109 | hash.0.clone() 110 | } 111 | } 112 | 113 | impl<'a> PrimaryKey<'a> for Hash { 114 | type Prefix = (); 115 | type SubPrefix = (); 116 | type Suffix = Self; 117 | type SuperSuffix = Self; 118 | 119 | fn key(&self) -> Vec> { 120 | vec![Key::Ref(self.0.as_ref())] 121 | } 122 | } 123 | 124 | impl KeyDeserialize for Hash { 125 | type Output = Hash; 126 | const KEY_ELEMS: u16 = 1; 127 | 128 | #[inline(always)] 129 | fn from_vec(value: Vec) -> StdResult { 130 | Ok(Hash(value)) 131 | } 132 | } 133 | 134 | impl KeyDeserialize for &Hash { 135 | type Output = Hash; 136 | const KEY_ELEMS: u16 = 1; 137 | 138 | #[inline(always)] 139 | fn from_vec(value: Vec) -> StdResult { 140 | Ok(Hash(value)) 141 | } 142 | } 143 | 144 | impl<'a> Prefixer<'a> for Hash { 145 | fn prefix(&self) -> Vec> { 146 | vec![Key::Ref(self.0.as_ref())] 147 | } 148 | } 149 | 150 | impl AsRef<[u8]> for Hash { 151 | #[inline] 152 | fn as_ref(&self) -> &[u8] { 153 | self.0.as_ref() 154 | } 155 | } 156 | 157 | #[cfg(test)] 158 | mod tests { 159 | use crate::crypto::Hash; 160 | 161 | #[test] 162 | fn vec_from_hash() { 163 | let h = Hash(vec![1, 2, 3]); 164 | let result: Vec = h.into(); 165 | assert_eq!(result, vec![1, 2, 3]); 166 | 167 | let h = &Hash(vec![3, 2, 1]); 168 | let result: Vec = h.into(); 169 | assert_eq!(result, vec![3, 2, 1]) 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/cursor.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::Hash; 2 | use crate::msg::Cursor; 3 | use crate::state::Object; 4 | use cosmwasm_std::{StdError, StdResult}; 5 | 6 | pub fn encode>(id: I) -> Cursor { 7 | bs58::encode(id).into_string() 8 | } 9 | 10 | pub fn decode>(cursor: I) -> StdResult { 11 | let raw = bs58::decode(cursor) 12 | .into_vec() 13 | .map_err(|err| StdError::parse_err("Cursor", err))?; 14 | 15 | String::from_utf8(raw).map_err(|err| StdError::parse_err("Cursor", err)) 16 | } 17 | 18 | pub trait AsCursor { 19 | fn encode_cursor(&self) -> Cursor; 20 | fn decode_cursor(_: Cursor) -> StdResult; 21 | } 22 | 23 | impl AsCursor for Object { 24 | fn encode_cursor(&self) -> Cursor { 25 | bs58::encode(&self.id).into_string() 26 | } 27 | 28 | fn decode_cursor(cursor: Cursor) -> StdResult { 29 | bs58::decode(cursor) 30 | .into_vec() 31 | .map(Into::into) 32 | .map_err(|err| StdError::parse_err("Cursor", err)) 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | 40 | #[test] 41 | fn proper_encode() { 42 | assert_eq!(encode(""), "".to_string()); 43 | assert_eq!(encode("an_id"), "BzZCCcK".to_string()); 44 | } 45 | 46 | #[test] 47 | fn proper_decode() { 48 | assert_eq!(decode(""), Ok("".to_string())); 49 | assert_eq!(decode("BzZCCcK"), Ok("an_id".to_string())); 50 | } 51 | 52 | #[test] 53 | fn invalid_decode() { 54 | assert_eq!( 55 | decode("?"), 56 | Err(StdError::parse_err( 57 | "Cursor", 58 | "provided string contained invalid character '?' at byte 0" 59 | )) 60 | ); 61 | assert_eq!( 62 | decode("VtB5VXc"), 63 | Err(StdError::parse_err( 64 | "Cursor", 65 | "invalid utf-8 sequence of 1 bytes from index 0" 66 | )) 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::compress::CompressionError; 2 | use crate::msg::CompressionAlgorithm; 3 | use cosmwasm_std::{StdError, Uint128}; 4 | use cw_utils::PaymentError; 5 | use thiserror::Error; 6 | 7 | #[derive(Debug, Error, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("{0}")] 13 | Bucket(#[from] BucketError), 14 | 15 | #[error("Object is pinned and cannot be forgotten")] 16 | ObjectPinned {}, 17 | 18 | #[error("Compression error: {0}")] 19 | CompressionError(String), 20 | 21 | #[error("{0}")] 22 | Payment(#[from] PaymentError), 23 | } 24 | 25 | #[derive(Debug, Eq, Error, PartialEq)] 26 | pub enum BucketError { 27 | #[error("Name of bucket could not be empty")] 28 | EmptyName, 29 | 30 | #[error("Maximum total size exceeded: {0} / {1}")] 31 | MaxTotalSizeLimitExceeded(Uint128, Uint128), 32 | 33 | #[error("Maximum objects number exceeded: {0} / {1}")] 34 | MaxObjectsLimitExceeded(Uint128, Uint128), 35 | 36 | #[error("Maximum object size exceeded: {0} / {1}")] 37 | MaxObjectSizeLimitExceeded(Uint128, Uint128), 38 | 39 | #[error("Maximum object pins number exceeded: {0} / {1}")] 40 | MaxObjectPinsLimitExceeded(Uint128, Uint128), 41 | 42 | #[error("Compression algorithm is not accepted: {0:?} (accepted: \"{1:?}\")")] 43 | CompressionAlgorithmNotAccepted(CompressionAlgorithm, Vec), 44 | } 45 | 46 | impl From for ContractError { 47 | fn from(err: CompressionError) -> Self { 48 | match err { 49 | CompressionError::Error(err) => ContractError::CompressionError(err), 50 | } 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_bucket_error_messages() { 56 | let cases = vec![ 57 | (ContractError::Std(StdError::generic_err("Software failure. Press left mouse button to continue. Guru Mediation #8000000B.0000AAC00")), 58 | "Generic error: Software failure. Press left mouse button to continue. Guru Mediation #8000000B.0000AAC00" 59 | ), 60 | ( 61 | ContractError::Bucket(BucketError::EmptyName), 62 | "Name of bucket could not be empty", 63 | ), 64 | ( 65 | ContractError::Bucket(BucketError::MaxTotalSizeLimitExceeded( 66 | 200u8.into(), 67 | 100u8.into(), 68 | )), 69 | "Maximum total size exceeded: 200 / 100", 70 | ), 71 | ( 72 | ContractError::Bucket(BucketError::MaxObjectsLimitExceeded(42u8.into(), 40u8.into())), 73 | "Maximum objects number exceeded: 42 / 40", 74 | ), 75 | ( 76 | ContractError::Bucket(BucketError::MaxObjectSizeLimitExceeded( 77 | 603u16.into(), 78 | 111u16.into(), 79 | )), 80 | "Maximum object size exceeded: 603 / 111", 81 | ), 82 | ( 83 | ContractError::Bucket(BucketError::MaxObjectPinsLimitExceeded(5u8.into(), 2u8.into())), 84 | "Maximum object pins number exceeded: 5 / 2", 85 | ), 86 | ( 87 | ContractError::Bucket(BucketError::CompressionAlgorithmNotAccepted( 88 | CompressionAlgorithm::Snappy, 89 | vec![CompressionAlgorithm::Passthrough], 90 | )), 91 | "Compression algorithm is not accepted: Snappy (accepted: \"[Passthrough]\")", 92 | ), 93 | (ContractError::ObjectPinned {}, "Object is pinned and cannot be forgotten"), 94 | ( 95 | ContractError::CompressionError("Insufficient ch'i to compress file".to_string()), 96 | "Compression error: Insufficient ch'i to compress file", 97 | ), 98 | ( 99 | CompressionError::Error("Cannot compress empty data".to_string()).into(), 100 | "Compression error: Cannot compress empty data", 101 | ), 102 | ]; 103 | 104 | for (error, expected_message) in cases { 105 | assert_eq!(error.to_string(), expected_message); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /contracts/axone-objectarium/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod compress; 2 | pub mod contract; 3 | pub mod crypto; 4 | mod cursor; 5 | mod error; 6 | pub mod msg; 7 | mod pagination; 8 | pub mod state; 9 | 10 | pub use crate::error::ContractError; 11 | -------------------------------------------------------------------------------- /docs/axone-law-stone.md: -------------------------------------------------------------------------------- 1 | # Law Stone 2 | 3 | ## Overview 4 | 5 | The `axone-law-stone` smart contract aims to provide GaaS (i.e. Governance as a Service) in any [Cosmos blockchains](https://cosmos.network/) using the [CosmWasm](https://cosmwasm.com/) framework and the [Logic](https://docs.axone.xyz/modules/next/logic) AXONE module. 6 | 7 | This contract is built around a Prolog program describing the law by rules and facts. The law stone is immutable, this means it can only be questioned, there are no update mechanisms. 8 | 9 | The `axone-law-stone` responsibility is to guarantee the availability of its rules in order to question them, but not to ensure the rules application. 10 | 11 | To ensure reliability over time, the associated Prolog program is stored and pinned in a `axone-objectarium` contract. Moreover, all the eventual loaded files must be stored in a `axone-objectarium` contract as well, allowing the contract to pin them. 12 | 13 | To be able to free the underlying resources (i.e. objects in `axone-objectarium`) if not used anymore, the contract admin can break the stone. 14 | 15 | ➡️ Checkout the [examples](https://github.com/axone-protocol/contracts/tree/main/contracts/axone-law-stone/examples/) for usage information. 16 | 17 | ## InstantiateMsg 18 | 19 | Instantiate message 20 | 21 | | parameter | description | 22 | | ----------------- | ------------------------------------------------------------------------------------------------------ | 23 | | `program` | _(Required.) _ **[Binary](#binary)**. The Prolog program carrying law rules and facts. | 24 | | `storage_address` | _(Required.) _ **string**. The `axone-objectarium` contract address on which to store the law program. | 25 | 26 | ## ExecuteMsg 27 | 28 | Execute messages 29 | 30 | ### ExecuteMsg::BreakStone 31 | 32 | Break the stone making this contract unusable, by clearing all the related resources: - Unpin all the pinned objects on `axone-objectarium` contracts, if any. - Forget the main program (i.e. or at least unpin it). 33 | 34 | Only the creator address (the address that instantiated the contract) is authorized to invoke this message. If already broken, this is a no-op. 35 | 36 | | parameter | description | 37 | | ------------- | -------------------------- | 38 | | `break_stone` | _(Required.) _ **object**. | 39 | 40 | ## QueryMsg 41 | 42 | Query messages 43 | 44 | ### QueryMsg::Ask 45 | 46 | Submits a Prolog query string to the `Logic` module, evaluating it against the law program associated with this contract. 47 | 48 | If the law stone is broken the query returns a response with the error `error(system_error(broken_law_stone),root)` set in the `answer` field. 49 | 50 | | parameter | description | 51 | | ----------- | -------------------------- | 52 | | `ask` | _(Required.) _ **object**. | 53 | | `ask.query` | _(Required.) _ **string**. | 54 | 55 | ### QueryMsg::Program 56 | 57 | Retrieves the location metadata of the law program bound to this contract. 58 | 59 | This includes the contract address of the `objectarium` and the program object ID, where the law program's code can be accessed. 60 | 61 | | parameter | description | 62 | | --------- | -------------------------- | 63 | | `program` | _(Required.) _ **object**. | 64 | 65 | ### QueryMsg::ProgramCode 66 | 67 | Fetches the raw code of the law program tied to this contract. 68 | 69 | If the law stone is broken, the query may fail if the program is no longer available in the `Objectarium`. 70 | 71 | | parameter | description | 72 | | -------------- | -------------------------- | 73 | | `program_code` | _(Required.) _ **object**. | 74 | 75 | ## Responses 76 | 77 | ### ask 78 | 79 | | property | description | 80 | | ------------- | ---------------------------- | 81 | | `answer` | **[Answer](#answer)\|null**. | 82 | | `gas_used` | _(Required.) _ **integer**. | 83 | | `height` | _(Required.) _ **integer**. | 84 | | `user_output` | **string\|null**. | 85 | 86 | ### program 87 | 88 | ProgramResponse carry elements to locate the program in a `axone-objectarium` contract. 89 | 90 | | property | description | 91 | | ----------------- | ------------------------------------------------------------------------------------------------------- | 92 | | `object_id` | _(Required.) _ **string**. The program object id in the `axone-objectarium` contract. | 93 | | `storage_address` | _(Required.) _ **string**. The `axone-objectarium` contract address on which the law program is stored. | 94 | 95 | ### program_code 96 | 97 | Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline. 98 | 99 | This is only needed as serde-json-\{core,wasm\} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>. 100 | 101 | | type | 102 | | ----------- | 103 | | **string**. | 104 | 105 | ## Definitions 106 | 107 | ### Answer 108 | 109 | | property | description | 110 | | ----------- | -------------------------------------------------- | 111 | | `has_more` | _(Required.) _ **boolean**. | 112 | | `results` | _(Required.) _ **Array<[Result](#result)>**. | 113 | | `variables` | _(Required.) _ **Array<string>**. | 114 | 115 | ### Binary 116 | 117 | A string containing Base64-encoded data. 118 | 119 | | type | 120 | | ----------- | 121 | | **string**. | 122 | 123 | ### Result 124 | 125 | | property | description | 126 | | --------------- | -------------------------------------------------------------- | 127 | | `error` | **string\|null**. | 128 | | `substitutions` | _(Required.) _ **Array<[Substitution](#substitution)>**. | 129 | 130 | ### Substitution 131 | 132 | | property | description | 133 | | ------------ | -------------------------- | 134 | | `expression` | _(Required.) _ **string**. | 135 | | `variable` | _(Required.) _ **string**. | 136 | 137 | --- 138 | 139 | _Rendered by [Fadroma](https://fadroma.tech) ([@fadroma/schema 1.1.0](https://www.npmjs.com/package/@fadroma/schema)) from `axone-law-stone.json` (`df827252e2e559ce`)_ 140 | -------------------------------------------------------------------------------- /etc/cognitarium.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axone-protocol/contracts/e6deef4de5b28a789bacce62d7dcc9a38bf68537/etc/cognitarium.webp -------------------------------------------------------------------------------- /etc/dataverse.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axone-protocol/contracts/e6deef4de5b28a789bacce62d7dcc9a38bf68537/etc/dataverse.webp -------------------------------------------------------------------------------- /etc/law-stone.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axone-protocol/contracts/e6deef4de5b28a789bacce62d7dcc9a38bf68537/etc/law-stone.webp -------------------------------------------------------------------------------- /etc/objectarium.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axone-protocol/contracts/e6deef4de5b28a789bacce62d7dcc9a38bf68537/etc/objectarium.webp -------------------------------------------------------------------------------- /packages/axone-cognitarium-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | edition = { workspace = true } 4 | name = "axone-cognitarium-client" 5 | 6 | description = "A client library for the Axone Cognitarium Smart Contract." 7 | homepage = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | version = { workspace = true } 11 | 12 | [dependencies] 13 | axone-cognitarium.workspace = true 14 | cosmwasm-std.workspace = true 15 | serde.workspace = true 16 | -------------------------------------------------------------------------------- /packages/axone-cognitarium-client/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/axone-cognitarium-client/README.md: -------------------------------------------------------------------------------- 1 | # Cognitarium client 2 | 3 | Package that holds components to interact with the `axone-cognitarium` contract. 4 | -------------------------------------------------------------------------------- /packages/axone-cognitarium-client/src/client.rs: -------------------------------------------------------------------------------- 1 | use axone_cognitarium::msg::{DataFormat, ExecuteMsg, QueryMsg, SelectResponse}; 2 | use axone_cognitarium::parser::SelectQuery; 3 | use cosmwasm_std::{ 4 | to_json_binary, Addr, Binary, Coin, CustomQuery, QuerierWrapper, QueryRequest, StdResult, 5 | WasmMsg, WasmQuery, 6 | }; 7 | use serde::de::DeserializeOwned; 8 | use serde::Serialize; 9 | 10 | pub struct CognitariumClient { 11 | address: Addr, 12 | } 13 | 14 | impl CognitariumClient { 15 | pub fn new(address: Addr) -> Self { 16 | Self { address } 17 | } 18 | 19 | pub fn select( 20 | &self, 21 | querier: QuerierWrapper<'_, C>, 22 | query: SelectQuery, 23 | ) -> StdResult { 24 | self.query_wasm(querier, &QueryMsg::Select { query }) 25 | } 26 | 27 | pub fn insert_data(&self, format: Option, data: Binary) -> StdResult { 28 | self.to_wasm_exec_msg(&ExecuteMsg::InsertData { format, data }, vec![]) 29 | } 30 | 31 | fn query_wasm(&self, querier: QuerierWrapper<'_, C>, msg: &T) -> StdResult 32 | where 33 | C: CustomQuery, 34 | T: Serialize + ?Sized, 35 | U: DeserializeOwned, 36 | { 37 | querier.query(&QueryRequest::Wasm(self.to_wasm_query_msg(msg)?)) 38 | } 39 | 40 | fn to_wasm_exec_msg(&self, msg: &T, funds: Vec) -> StdResult 41 | where 42 | T: Serialize + ?Sized, 43 | { 44 | Ok(WasmMsg::Execute { 45 | contract_addr: self.address.to_string(), 46 | msg: to_json_binary(msg)?, 47 | funds, 48 | }) 49 | } 50 | 51 | fn to_wasm_query_msg(&self, msg: &T) -> StdResult 52 | where 53 | T: Serialize + ?Sized, 54 | { 55 | Ok(WasmQuery::Smart { 56 | contract_addr: self.address.to_string(), 57 | msg: to_json_binary(msg)?, 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/axone-cognitarium-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod client; 2 | 3 | pub use client::*; 4 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | edition = { workspace = true } 4 | name = "axone-logic-bindings" 5 | 6 | description = "A library defining the bindings for querying the AXONE logic module." 7 | homepage = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | version = { workspace = true } 11 | 12 | [dependencies] 13 | cosmwasm-std.workspace = true 14 | schemars.workspace = true 15 | serde.workspace = true 16 | thiserror.workspace = true 17 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/README.md: -------------------------------------------------------------------------------- 1 | # Logic bindings 2 | 3 | Package that holds all bindings for querying the AXONE logic module. 4 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/atom.rs: -------------------------------------------------------------------------------- 1 | /// Convert a Rust string to a Prolog atom. 2 | pub fn as_prolog_atom(s: &str) -> String { 3 | let mut escaped = String::with_capacity(s.len() + 2); 4 | escaped.push('\''); 5 | for c in s.chars() { 6 | if c == '\'' { 7 | escaped.push('\\'); 8 | escaped.push(c); 9 | } else { 10 | escaped.push(c); 11 | } 12 | } 13 | escaped.push('\''); 14 | 15 | escaped 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::as_prolog_atom; 21 | 22 | #[test] 23 | fn test_as_prolog_atom() { 24 | let test_cases = vec![ 25 | ("empty string", "", "''"), 26 | ("simple case", "hello", "'hello'"), 27 | ("space in the string", "hello world", "'hello world'"), 28 | ("single quote in the middle", "foo'bar", "'foo\\'bar'"), 29 | ("enclosed single quotes", "'foo bar'", "'\\'foo bar\\''"), 30 | ("cosmwasm URI", "cosmwasm:name:address?query=%7B%22object_data%22%3A%7B%22id%22%3A%221a88ca1632c7323c0aa594000cda26ed9f48b36351c29c3d1e35e0a0474e862e%22%7D%7D", "'cosmwasm:name:address?query=%7B%22object_data%22%3A%7B%22id%22%3A%221a88ca1632c7323c0aa594000cda26ed9f48b36351c29c3d1e35e0a0474e862e%22%7D%7D'") 31 | ]; 32 | 33 | for (_, input, expected) in test_cases { 34 | let actual = as_prolog_atom(input); 35 | assert_eq!( 36 | actual, expected, 37 | "as_prolog_atom({:?}) should produce {:?}, but got {:?}", 38 | input, expected, actual 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::string::FromUtf8Error; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Eq, Error, PartialEq)] 5 | pub enum TermParseError { 6 | #[error("Value is not UTF-8 encoded: {0}")] 7 | NotUtf8Value(FromUtf8Error), 8 | 9 | #[error("Reach unexpected EOF")] 10 | Eof, 11 | 12 | #[error("Expected ',' or end of sequence and got: '{0}'")] 13 | ExpectedSeqToken(char), 14 | 15 | #[error("Unexpected end of array or tuple")] 16 | UnexpectedEndOfSeq, 17 | 18 | #[error("Forbidden token in value: '{0}'")] 19 | UnexpectedValueToken(char), 20 | 21 | #[error("Unexpected root token: '{0}'")] 22 | UnexpectedRootToken(char), 23 | 24 | #[error("Empty value in array or tuple")] 25 | EmptyValue, 26 | 27 | #[error("Empty tuple")] 28 | EmptyTuple, 29 | } 30 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | mod query; 3 | mod term_parser; 4 | 5 | pub use atom::as_prolog_atom; 6 | pub use query::{Answer, AskResponse, LogicCustomQuery, Result, Substitution}; 7 | pub use term_parser::TermValue; 8 | 9 | // Exposed for testing only 10 | // Both unit tests and integration tests are compiled to native code, so everything in here does not need to compile to Wasm. 11 | mod atom; 12 | #[cfg(not(target_arch = "wasm32"))] 13 | pub mod testing; 14 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/query.rs: -------------------------------------------------------------------------------- 1 | use crate::error::TermParseError; 2 | use crate::term_parser::{from_str, TermValue}; 3 | use cosmwasm_std::CustomQuery; 4 | use schemars::JsonSchema; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] 8 | #[serde(rename_all = "snake_case")] 9 | pub enum LogicCustomQuery { 10 | Ask { program: String, query: String }, 11 | } 12 | 13 | impl CustomQuery for LogicCustomQuery {} 14 | 15 | #[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] 16 | #[serde(rename_all = "snake_case")] 17 | pub struct AskResponse { 18 | pub height: u64, 19 | pub gas_used: u64, 20 | pub answer: Option, 21 | pub user_output: Option, 22 | } 23 | 24 | #[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] 25 | #[serde(rename_all = "snake_case")] 26 | pub struct Answer { 27 | pub has_more: bool, 28 | pub variables: Vec, 29 | pub results: Vec, 30 | } 31 | 32 | impl Answer { 33 | /// Create a new Answer with an error message. 34 | pub fn from_error(error: String) -> Self { 35 | Self { 36 | has_more: false, 37 | variables: vec![], 38 | results: vec![Result { 39 | error: Some(error), 40 | substitutions: vec![], 41 | }], 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] 47 | #[serde(rename_all = "snake_case")] 48 | pub struct Result { 49 | pub error: Option, 50 | pub substitutions: Vec, 51 | } 52 | 53 | #[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] 54 | #[serde(rename_all = "snake_case")] 55 | pub struct Substitution { 56 | pub variable: String, 57 | pub expression: String, 58 | } 59 | 60 | impl Substitution { 61 | pub fn parse_expression(self) -> std::result::Result { 62 | from_str(self.expression.as_str()) 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use super::*; 69 | 70 | #[test] 71 | fn term_parse() { 72 | assert_eq!( 73 | Substitution { 74 | variable: "X".to_string(), 75 | expression: "'hello'".to_string(), 76 | } 77 | .parse_expression(), 78 | Ok(TermValue::Value("hello".to_string())) 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/testing/mock.rs: -------------------------------------------------------------------------------- 1 | use crate::LogicCustomQuery; 2 | use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage}; 3 | use cosmwasm_std::{Coin, OwnedDeps, QuerierResult}; 4 | use std::marker::PhantomData; 5 | 6 | pub fn mock_dependencies_with_logic_handler( 7 | handler: LH, 8 | ) -> OwnedDeps, LogicCustomQuery> 9 | where 10 | LH: Fn(&LogicCustomQuery) -> QuerierResult + 'static, 11 | { 12 | OwnedDeps { 13 | storage: MockStorage::default(), 14 | api: MockApi::default(), 15 | querier: MockLogicQuerier::new(LogicQuerier::new(Box::new(handler)), &[]), 16 | custom_query_type: PhantomData, 17 | } 18 | } 19 | 20 | trait MockLogicQuerier { 21 | fn new(logic: LogicQuerier, balances: &[(&str, &[Coin])]) -> Self; 22 | } 23 | 24 | impl MockLogicQuerier for MockQuerier { 25 | fn new(logic: LogicQuerier, balances: &[(&str, &[Coin])]) -> Self { 26 | MockQuerier::new(balances).with_custom_handler(Box::new(logic.handler)) 27 | } 28 | } 29 | 30 | struct LogicQuerier { 31 | /// A handler to handle Logic queries. This is set to a dummy handler that 32 | /// always return a successful foo / bar response by default. Update it via `update_handler`. 33 | /// 34 | /// Use box to avoid the need of generic type. 35 | handler: Box Fn(&'a LogicCustomQuery) -> QuerierResult>, 36 | } 37 | 38 | impl LogicQuerier { 39 | fn new(handler: Box Fn(&'a LogicCustomQuery) -> QuerierResult>) -> Self { 40 | Self { handler } 41 | } 42 | 43 | #[allow(dead_code)] 44 | fn update_handler(&mut self, handler: LH) 45 | where 46 | LH: Fn(&LogicCustomQuery) -> QuerierResult + 'static, 47 | { 48 | self.handler = Box::from(handler); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/axone-logic-bindings/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(target_arch = "wasm32"))] 2 | 3 | // Exposed for testing only 4 | // Both unit tests and integration tests are compiled to native code, so everything in here does not need to compile to Wasm. 5 | 6 | pub mod mock; 7 | -------------------------------------------------------------------------------- /packages/axone-objectarium-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | edition = { workspace = true } 4 | name = "axone-objectarium-client" 5 | 6 | description = "A client library for the Axone Objectarium Smart Contract." 7 | homepage = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | version = { workspace = true } 11 | 12 | [dependencies] 13 | axone-objectarium.workspace = true 14 | axone-wasm.workspace = true 15 | cosmwasm-std.workspace = true 16 | serde.workspace = true 17 | -------------------------------------------------------------------------------- /packages/axone-objectarium-client/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/axone-objectarium-client/README.md: -------------------------------------------------------------------------------- 1 | # Objectarium client 2 | 3 | Package that holds components to interact with the `axone-objectarium` contract. 4 | -------------------------------------------------------------------------------- /packages/axone-objectarium-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod object; 2 | 3 | pub use object::ObjectRef; 4 | -------------------------------------------------------------------------------- /packages/axone-rdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | edition = { workspace = true } 4 | name = "axone-rdf" 5 | 6 | description = "Utility library offering essential components for efficiently handling RDF data within the Axone protocol." 7 | homepage = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | version = { workspace = true } 11 | 12 | [dependencies] 13 | base16ct = { version = "0.2.0", features = ["alloc"] } 14 | cosmwasm-std.workspace = true 15 | itertools = "0.14.0" 16 | rio_api.workspace = true 17 | rio_turtle.workspace = true 18 | rio_xml.workspace = true 19 | sha2 = "0.10.9" 20 | thiserror.workspace = true 21 | -------------------------------------------------------------------------------- /packages/axone-rdf/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/axone-rdf/README.md: -------------------------------------------------------------------------------- 1 | # RDF 2 | 3 | Package that holds useful components to manage with `RDF` data, typically reading / writing. 4 | -------------------------------------------------------------------------------- /packages/axone-rdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod dataset; 2 | pub mod normalize; 3 | pub mod owned_model; 4 | pub mod serde; 5 | pub mod uri; 6 | -------------------------------------------------------------------------------- /packages/axone-rdf/src/owned_model.rs: -------------------------------------------------------------------------------- 1 | use rio_api::model::{BlankNode, GraphName, Literal, NamedNode, Quad, Subject, Term}; 2 | 3 | #[derive(Debug)] 4 | pub struct RDFStarUnsupported; 5 | 6 | pub struct OwnedQuad { 7 | subject: OwnedSubject, 8 | predicate: String, 9 | object: OwnedTerm, 10 | graph_name: Option, 11 | } 12 | 13 | impl TryFrom> for OwnedQuad { 14 | type Error = RDFStarUnsupported; 15 | 16 | fn try_from(value: Quad<'_>) -> Result { 17 | Ok(Self { 18 | subject: value.subject.try_into()?, 19 | predicate: value.predicate.iri.to_owned(), 20 | object: value.object.try_into()?, 21 | graph_name: value.graph_name.map(OwnedGraphName::from), 22 | }) 23 | } 24 | } 25 | 26 | impl<'a> From<&'a OwnedQuad> for Quad<'a> { 27 | fn from(value: &'a OwnedQuad) -> Self { 28 | Self { 29 | subject: (&value.subject).into(), 30 | predicate: NamedNode { 31 | iri: value.predicate.as_str(), 32 | }, 33 | object: (&value.object).into(), 34 | graph_name: value.graph_name.as_ref().map(GraphName::from), 35 | } 36 | } 37 | } 38 | 39 | pub enum Id { 40 | Named(String), 41 | Blank(String), 42 | } 43 | 44 | pub type OwnedSubject = Id; 45 | 46 | impl TryFrom> for OwnedSubject { 47 | type Error = RDFStarUnsupported; 48 | 49 | fn try_from(value: Subject<'_>) -> Result { 50 | Ok(match value { 51 | Subject::NamedNode(n) => Self::Named(n.iri.to_owned()), 52 | Subject::BlankNode(n) => Self::Blank(n.id.to_owned()), 53 | Subject::Triple(_) => Err(RDFStarUnsupported {})?, 54 | }) 55 | } 56 | } 57 | 58 | impl<'a> From<&'a OwnedSubject> for Subject<'a> { 59 | fn from(value: &'a OwnedSubject) -> Self { 60 | match value { 61 | OwnedSubject::Named(iri) => NamedNode { iri }.into(), 62 | OwnedSubject::Blank(id) => BlankNode { id }.into(), 63 | } 64 | } 65 | } 66 | 67 | pub type OwnedGraphName = Id; 68 | 69 | impl From> for OwnedGraphName { 70 | fn from(value: GraphName<'_>) -> Self { 71 | match value { 72 | GraphName::NamedNode(n) => Self::Named(n.iri.to_owned()), 73 | GraphName::BlankNode(n) => Self::Blank(n.id.to_owned()), 74 | } 75 | } 76 | } 77 | 78 | impl<'a> From<&'a OwnedGraphName> for GraphName<'a> { 79 | fn from(value: &'a OwnedGraphName) -> Self { 80 | match value { 81 | OwnedGraphName::Named(iri) => NamedNode { iri }.into(), 82 | OwnedGraphName::Blank(id) => BlankNode { id }.into(), 83 | } 84 | } 85 | } 86 | 87 | pub enum OwnedTerm { 88 | Named(String), 89 | Blank(String), 90 | Literal(OwnedLiteral), 91 | } 92 | 93 | impl TryFrom> for OwnedTerm { 94 | type Error = RDFStarUnsupported; 95 | 96 | fn try_from(value: Term<'_>) -> Result { 97 | Ok(match value { 98 | Term::NamedNode(n) => OwnedTerm::Named(n.iri.to_owned()), 99 | Term::BlankNode(n) => OwnedTerm::Blank(n.id.to_owned()), 100 | Term::Literal(l) => OwnedTerm::Literal(l.into()), 101 | Term::Triple(_) => Err(RDFStarUnsupported)?, 102 | }) 103 | } 104 | } 105 | 106 | impl<'a> From<&'a OwnedTerm> for Term<'a> { 107 | fn from(value: &'a OwnedTerm) -> Self { 108 | match value { 109 | OwnedTerm::Named(iri) => NamedNode { iri }.into(), 110 | OwnedTerm::Blank(id) => BlankNode { id }.into(), 111 | OwnedTerm::Literal(l) => Term::Literal(l.into()), 112 | } 113 | } 114 | } 115 | 116 | pub enum OwnedLiteral { 117 | Simple(String), 118 | LanguageTaggedString { value: String, language: String }, 119 | Typed { value: String, datatype: String }, 120 | } 121 | 122 | impl From> for OwnedLiteral { 123 | fn from(value: Literal<'_>) -> Self { 124 | match value { 125 | Literal::Simple { value } => OwnedLiteral::Simple(value.to_owned()), 126 | Literal::LanguageTaggedString { value, language } => { 127 | OwnedLiteral::LanguageTaggedString { 128 | value: value.to_owned(), 129 | language: language.to_owned(), 130 | } 131 | } 132 | Literal::Typed { value, datatype } => OwnedLiteral::Typed { 133 | value: value.to_owned(), 134 | datatype: datatype.iri.to_owned(), 135 | }, 136 | } 137 | } 138 | } 139 | 140 | impl<'a> From<&'a OwnedLiteral> for Literal<'a> { 141 | fn from(l: &'a OwnedLiteral) -> Self { 142 | match l { 143 | OwnedLiteral::Simple(value) => Literal::Simple { value }, 144 | OwnedLiteral::LanguageTaggedString { value, language } => { 145 | Literal::LanguageTaggedString { value, language } 146 | } 147 | OwnedLiteral::Typed { value, datatype } => Literal::Typed { 148 | value, 149 | datatype: NamedNode { iri: datatype }, 150 | }, 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /packages/axone-rdf/src/serde.rs: -------------------------------------------------------------------------------- 1 | use crate::owned_model::OwnedQuad; 2 | use rio_api::formatter::TriplesFormatter; 3 | use rio_api::model::{Quad, Triple}; 4 | use rio_api::parser::{QuadsParser, TriplesParser}; 5 | use rio_turtle::{ 6 | NQuadsFormatter, NQuadsParser, NTriplesFormatter, NTriplesParser, TurtleError, TurtleFormatter, 7 | TurtleParser, 8 | }; 9 | use rio_xml::{RdfXmlError, RdfXmlFormatter, RdfXmlParser}; 10 | use std::io::{self, BufRead}; 11 | use thiserror::Error; 12 | 13 | pub struct TripleReader { 14 | parser: TriplesParserKind, 15 | } 16 | 17 | pub struct NQuadsReader { 18 | parser: NQuadsParser, 19 | } 20 | 21 | #[derive(Debug, Error)] 22 | pub enum NQuadsReadError { 23 | #[error("RDF Star notation not supported")] 24 | RDFStarUnsupported, 25 | 26 | #[error("Couldn't parse rdf: {0}")] 27 | Parse(#[from] TurtleError), 28 | } 29 | 30 | pub struct TripleWriter { 31 | writer: TriplesWriterKind, 32 | } 33 | 34 | #[allow(clippy::large_enum_variant)] 35 | pub enum TriplesParserKind { 36 | NTriples(NTriplesParser), 37 | Turtle(TurtleParser), 38 | RdfXml(RdfXmlParser), 39 | NQuads(NQuadsParser), 40 | } 41 | 42 | pub enum TriplesWriterKind { 43 | NTriples(NTriplesFormatter), 44 | Turtle(TurtleFormatter), 45 | RdfXml(io::Result>), 46 | NQuads(NQuadsFormatter), 47 | } 48 | 49 | pub enum DataFormat { 50 | /// Represents a [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) format. 51 | RDFXml, 52 | /// Represents a [Turtle](https://www.w3.org/TR/turtle/) format. 53 | Turtle, 54 | /// Represents a [N-Triples](https://www.w3.org/TR/n-triples/) format. 55 | NTriples, 56 | /// Represents a [N-Quads](https://www.w3.org/TR/n-quads/) format. 57 | NQuads, 58 | } 59 | 60 | impl TripleReader { 61 | pub fn new(format: &DataFormat, src: R) -> Self { 62 | TripleReader { 63 | parser: match format { 64 | DataFormat::RDFXml => TriplesParserKind::RdfXml(RdfXmlParser::new(src, None)), 65 | DataFormat::Turtle => TriplesParserKind::Turtle(TurtleParser::new(src, None)), 66 | DataFormat::NTriples => TriplesParserKind::NTriples(NTriplesParser::new(src)), 67 | DataFormat::NQuads => TriplesParserKind::NQuads(NQuadsParser::new(src)), 68 | }, 69 | } 70 | } 71 | 72 | pub fn read_all(&mut self, mut use_fn: UF) -> Result<(), E> 73 | where 74 | UF: FnMut(Triple<'_>) -> Result<(), E>, 75 | E: From + From, 76 | { 77 | match &mut self.parser { 78 | TriplesParserKind::NTriples(parser) => parser.parse_all(&mut use_fn), 79 | TriplesParserKind::Turtle(parser) => parser.parse_all(&mut use_fn), 80 | TriplesParserKind::RdfXml(parser) => parser.parse_all(&mut use_fn), 81 | TriplesParserKind::NQuads(parser) => { 82 | parser.parse_all(&mut |quad: Quad<'_>| -> Result<(), E> { 83 | use_fn(Triple { 84 | subject: quad.subject, 85 | predicate: quad.predicate, 86 | object: quad.object, 87 | }) 88 | }) 89 | } 90 | } 91 | } 92 | } 93 | 94 | impl NQuadsReader { 95 | pub fn new(src: R) -> Self { 96 | NQuadsReader { 97 | parser: NQuadsParser::new(src), 98 | } 99 | } 100 | 101 | pub fn read_all(&mut self) -> Result, NQuadsReadError> { 102 | let mut quads = vec![]; 103 | 104 | self.parser 105 | .parse_all(&mut |quad| -> Result<(), NQuadsReadError> { 106 | quads.push( 107 | quad.try_into() 108 | .map_err(|_| NQuadsReadError::RDFStarUnsupported)?, 109 | ); 110 | Ok(()) 111 | })?; 112 | 113 | Ok(quads) 114 | } 115 | } 116 | 117 | impl TripleWriter { 118 | pub fn new(format: &DataFormat, dst: W) -> Self { 119 | TripleWriter { 120 | writer: match format { 121 | DataFormat::RDFXml => TriplesWriterKind::RdfXml(RdfXmlFormatter::new(dst)), 122 | DataFormat::Turtle => TriplesWriterKind::Turtle(TurtleFormatter::new(dst)), 123 | DataFormat::NTriples => TriplesWriterKind::NTriples(NTriplesFormatter::new(dst)), 124 | DataFormat::NQuads => TriplesWriterKind::NQuads(NQuadsFormatter::new(dst)), 125 | }, 126 | } 127 | } 128 | 129 | pub fn write(&mut self, triple: &Triple<'_>) -> io::Result<()> { 130 | match &mut self.writer { 131 | TriplesWriterKind::Turtle(formatter) => formatter.format(triple), 132 | TriplesWriterKind::NTriples(formatter) => formatter.format(triple), 133 | TriplesWriterKind::NQuads(formatter) => { 134 | use rio_api::formatter::QuadsFormatter; 135 | 136 | let quad = &Quad { 137 | subject: triple.subject, 138 | predicate: triple.predicate, 139 | object: triple.object, 140 | graph_name: None, 141 | }; 142 | 143 | formatter.format(quad) 144 | } 145 | TriplesWriterKind::RdfXml(format_result) => match format_result { 146 | Ok(formatter) => formatter.format(triple), 147 | Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())), 148 | }, 149 | } 150 | } 151 | 152 | pub fn write_all(&mut self, triples: Vec<&Triple<'_>>) -> io::Result<()> { 153 | for triple in triples { 154 | self.write(triple)?; 155 | } 156 | Ok(()) 157 | } 158 | 159 | pub fn finish(self) -> io::Result { 160 | match self.writer { 161 | TriplesWriterKind::Turtle(formatter) => formatter.finish(), 162 | TriplesWriterKind::NTriples(formatter) => formatter.finish(), 163 | TriplesWriterKind::NQuads(formatter) => formatter.finish(), 164 | TriplesWriterKind::RdfXml(format_result) => match format_result { 165 | Ok(formatter) => formatter.finish(), 166 | Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())), 167 | }, 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /packages/axone-rdf/src/uri.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{StdError, StdResult}; 2 | use std::collections::HashMap; 3 | 4 | /// Explode a compacted URI (CURIE - URI with prefix) separating it from its prefix. 5 | pub fn explode_iri(iri: &str) -> StdResult<(String, String)> { 6 | let mut marker_index: Option = None; 7 | for delim in ['#', '/', ':'] { 8 | if let Some(index) = iri.rfind(delim) { 9 | marker_index = match marker_index { 10 | Some(i) => Some(i.max(index)), 11 | None => Some(index), 12 | } 13 | } 14 | } 15 | 16 | if let Some(index) = marker_index { 17 | return Ok((iri[..=index].to_string(), iri[index + 1..].to_string())); 18 | } 19 | 20 | Err(StdError::generic_err("Couldn't extract IRI namespace")) 21 | } 22 | 23 | /// Expand a compacted URI (CURIE - URI with prefix) to a full URI. 24 | pub fn expand_uri(curie: &str, prefixes: &HashMap) -> StdResult { 25 | let idx = curie 26 | .rfind(':') 27 | .ok_or_else(|| StdError::generic_err(format!("Malformed CURIE: {curie}")))?; 28 | 29 | let prefix = curie[..idx].to_string(); 30 | let namespace = prefixes 31 | .get(&prefix) 32 | .ok_or_else(|| StdError::generic_err(format!("Prefix not found: {prefix}")))?; 33 | let suffix = curie[idx + 1..].to_string(); 34 | 35 | Ok(format!("{namespace}{suffix}")) 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn proper_explode_iri() { 44 | assert_eq!( 45 | explode_iri("http://www.w3.org/2001/XMLSchema#dateTime"), 46 | Ok(( 47 | "http://www.w3.org/2001/XMLSchema#".to_string(), 48 | "dateTime".to_string() 49 | )) 50 | ); 51 | assert_eq!( 52 | explode_iri("https://ontology.axone.space/core/Governance"), 53 | Ok(( 54 | "https://ontology.axone.space/core/".to_string(), 55 | "Governance".to_string() 56 | )) 57 | ); 58 | assert_eq!( 59 | explode_iri( 60 | "did:key:0x04d1f1b8f8a7a28f9a5a254c326a963a22f5a5b5d5f5e5d5c5b5a5958575655" 61 | ), 62 | Ok(( 63 | "did:key:".to_string(), 64 | "0x04d1f1b8f8a7a28f9a5a254c326a963a22f5a5b5d5f5e5d5c5b5a5958575655".to_string() 65 | )) 66 | ); 67 | assert_eq!( 68 | explode_iri("wow:this/is#weird"), 69 | Ok(("wow:this/is#".to_string(), "weird".to_string())) 70 | ); 71 | assert_eq!( 72 | explode_iri("this#is:weird/too"), 73 | Ok(("this#is:weird/".to_string(), "too".to_string())) 74 | ); 75 | assert_eq!( 76 | explode_iri("this_doesn't_work"), 77 | Err(StdError::generic_err("Couldn't extract IRI namespace")) 78 | ); 79 | } 80 | 81 | #[test] 82 | fn test_expand_uri() { 83 | let prefixes = HashMap::from([ 84 | ("ex".to_string(), "http://example.com/".to_string()), 85 | ( 86 | "rdf".to_string(), 87 | "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(), 88 | ), 89 | ]); 90 | 91 | assert_eq!( 92 | expand_uri("ex:resource", &prefixes), 93 | Ok("http://example.com/resource".to_string()) 94 | ); 95 | 96 | assert_eq!( 97 | expand_uri("ex:", &prefixes), 98 | Ok("http://example.com/".to_string()) 99 | ); 100 | 101 | assert_eq!( 102 | expand_uri("unknown:resource", &prefixes), 103 | Err(StdError::generic_err("Prefix not found: unknown")) 104 | ); 105 | 106 | assert_eq!( 107 | expand_uri("malformed_curie:", &prefixes), 108 | Err(StdError::generic_err("Prefix not found: malformed_curie")) 109 | ); 110 | 111 | assert_eq!( 112 | expand_uri("malformed_curie", &prefixes), 113 | Err(StdError::generic_err("Malformed CURIE: malformed_curie")) 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /packages/axone-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = { workspace = true } 3 | edition = { workspace = true } 4 | name = "axone-wasm" 5 | 6 | description = "A library that defines CosmWasm URIs, enabling the identification of blockchain resources by referencing specific instantiated smart contracts." 7 | homepage = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | version = { workspace = true } 11 | 12 | [dependencies] 13 | form_urlencoded = "1.2.1" 14 | serde.workspace = true 15 | serde-json-wasm.workspace = true 16 | thiserror.workspace = true 17 | url = "2.5.4" 18 | -------------------------------------------------------------------------------- /packages/axone-wasm/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/axone-wasm/README.md: -------------------------------------------------------------------------------- 1 | # WASM 2 | 3 | A library that defines CosmWasm URIs, enabling the identification of blockchain resources by referencing specific 4 | instantiated smart contracts. 5 | -------------------------------------------------------------------------------- /packages/axone-wasm/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | use url::ParseError; 3 | 4 | #[derive(Debug, Eq, Error, PartialEq)] 5 | pub enum CosmwasmUriError { 6 | #[error("{0}")] 7 | ParseURI(#[from] ParseError), 8 | 9 | #[error("{0}")] 10 | ParseQuery(String), 11 | 12 | #[error("{0}")] 13 | SerializeQuery(String), 14 | 15 | #[error("Malformed URI: {0}")] 16 | Malformed(String), 17 | } 18 | -------------------------------------------------------------------------------- /packages/axone-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod uri; 3 | -------------------------------------------------------------------------------- /packages/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["AXONE"] 3 | edition = "2021" 4 | name = "testing" 5 | publish = false 6 | version = "5.0.0" 7 | 8 | [dependencies] 9 | cosmwasm-std.workspace = true 10 | -------------------------------------------------------------------------------- /packages/testing/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.schema] 2 | -------------------------------------------------------------------------------- /packages/testing/src/addr.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::testing::MockApi; 2 | use cosmwasm_std::Addr; 3 | 4 | pub const CREATOR: &str = "creator"; 5 | pub const SENDER: &str = "sender"; 6 | pub const OWNER: &str = "owner"; 7 | 8 | pub fn addr(input: &str) -> Addr { 9 | MockApi::default().addr_make(input) 10 | } 11 | -------------------------------------------------------------------------------- /packages/testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(target_arch = "wasm32"))] 2 | 3 | pub mod addr; 4 | pub mod mock; 5 | -------------------------------------------------------------------------------- /packages/testing/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate::addr::addr; 2 | use cosmwasm_std::testing::{mock_env, MOCK_CONTRACT_ADDR}; 3 | use cosmwasm_std::Env; 4 | 5 | pub fn mock_env_addr() -> Env { 6 | let mut env = mock_env(); 7 | env.contract.address = addr(MOCK_CONTRACT_ADDR); 8 | env 9 | } 10 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/.ffizer.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | - name: contract_name 3 | default_value: axone-foo 4 | 5 | scripts: 6 | - message: | 7 | 🖨️ Smart contract 🚀 {{ contract_name }} scaffolded! Enjoy! 8 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/Cargo.ffizer.hbs.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["AXONE"] 3 | edition = "2021" 4 | name = "{{ contract_name }}" 5 | rust-version = "1.69" 6 | version = "0.0.1" 7 | 8 | exclude = [ 9 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 10 | "contract.wasm", 11 | "hash.txt", 12 | ] 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [lib] 17 | crate-type = ["cdylib", "rlib"] 18 | 19 | [profile.release] 20 | codegen-units = 1 21 | debug = false 22 | debug-assertions = false 23 | incremental = false 24 | lto = true 25 | opt-level = 3 26 | overflow-checks = true 27 | panic = 'abort' 28 | rpath = false 29 | 30 | [dependencies] 31 | axone-logic-bindings.workspace = true 32 | axone-objectarium.workspace = true 33 | axone-objectarium-client.workspace = true 34 | cosmwasm-schema.workspace = true 35 | cosmwasm-std.workspace = true 36 | cosmwasm-storage.workspace = true 37 | cw-storage-plus.workspace = true 38 | cw-utils.workspace = true 39 | cw2.workspace = true 40 | schemars.workspace = true 41 | serde.workspace = true 42 | thiserror.workspace = true 43 | 44 | [dev-dependencies] 45 | cw-multi-test.workspace = true 46 | 47 | [features] 48 | # for more explicit tests, cargo test --features=backtraces 49 | backtraces = ["cosmwasm-std/backtraces"] 50 | # use library feature to disable all instantiate/execute/query exports 51 | library = [] 52 | 53 | [package.metadata.scripts] 54 | optimize = """docker run --rm -v "$(pwd)":/code \ 55 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 56 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 57 | cosmwasm/rust-optimizer:0.12.10 58 | """ 59 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.generate_schema] 2 | args = ["run", "--bin", "schema"] 3 | command = "cargo" 4 | 5 | [tasks.schema] 6 | dependencies = ["generate_schema"] 7 | script = ''' 8 | SCHEMA=$(find schema -type f -maxdepth 1 -name '*.json' -print0) 9 | TITLE=$(jq -r .contract_name $SCHEMA) 10 | jq --arg description "$(cat README.md)" '. + {description: $description}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 11 | jq --arg title $TITLE '. + {title: $title}' $SCHEMA > $SCHEMA.tmp && mv $SCHEMA.tmp $SCHEMA 12 | ''' 13 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/README.ffizer.hbs.md: -------------------------------------------------------------------------------- 1 | # {{ contract_name }} 2 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/bin/schema.ffizer.hbs.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use {{ replace contract_name "-" "_" }}::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | execute: ExecuteMsg, 9 | query: QueryMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult}; 4 | use cw2::set_contract_version; 5 | 6 | use crate::error::ContractError; 7 | use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 8 | 9 | const CONTRACT_NAME: &str = concat!("crates.io:", env!("CARGO_PKG_NAME")); 10 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); 11 | 12 | #[cfg_attr(not(feature = "library"), entry_point)] 13 | pub fn instantiate( 14 | deps: DepsMut<'_>, 15 | _env: Env, 16 | _info: MessageInfo, 17 | _msg: InstantiateMsg, 18 | ) -> Result { 19 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; 20 | 21 | Err(StdError::generic_err("Not implemented").into()) 22 | } 23 | 24 | #[cfg_attr(not(feature = "library"), entry_point)] 25 | pub fn execute( 26 | _deps: DepsMut<'_>, 27 | _env: Env, 28 | _info: MessageInfo, 29 | _msg: ExecuteMsg, 30 | ) -> Result { 31 | Err(StdError::generic_err("Not implemented").into()) 32 | } 33 | 34 | pub mod execute {} 35 | 36 | #[cfg_attr(not(feature = "library"), entry_point)] 37 | pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { 38 | Err(StdError::generic_err("Not implemented")) 39 | } 40 | 41 | pub mod query {} 42 | 43 | #[cfg(test)] 44 | mod tests {} 45 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error, PartialEq)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | } 9 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod state; 5 | 6 | pub use crate::error::ContractError; 7 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | 3 | /// Instantiate message 4 | #[cw_serde] 5 | pub struct InstantiateMsg {} 6 | 7 | /// Execute messages 8 | #[cw_serde] 9 | pub enum ExecuteMsg { 10 | /// # Foo 11 | Foo, 12 | } 13 | 14 | /// Query messages 15 | #[cw_serde] 16 | #[derive(QueryResponses)] 17 | pub enum QueryMsg { 18 | /// # Bar 19 | #[returns(BarResponse)] 20 | Bar { foo: String }, 21 | } 22 | 23 | /// # BarResponse 24 | #[cw_serde] 25 | pub struct BarResponse { 26 | /// The foo value 27 | pub foo: String, 28 | } 29 | -------------------------------------------------------------------------------- /templates/axone-smart-contract/{{ contract_name }}/src/state.rs: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------