├── .cargo └── config ├── .github ├── actionlint.yaml ├── e2e │ ├── docker-compose.yml │ └── ipfs.sh ├── release-please │ ├── config.json │ └── manifest.json ├── renovate.json └── workflows │ ├── e2e.yml │ ├── lint.yml │ ├── release.yml │ ├── run-tests.yml │ ├── snapshot.yml │ └── tests.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aqua-tests ├── .fluence │ └── secrets │ │ └── test_put_get_dag.txt ├── .gitignore ├── README.md ├── config.py ├── fluence.yaml ├── getDefaultPeers.js ├── package-lock.json ├── package.json ├── requirements.txt ├── src │ └── aqua │ │ └── main.aqua └── test_aqua.py ├── aqua ├── README.md ├── ipfs-api.aqua ├── ipfs.aqua ├── package-lock.json └── package.json ├── builtin-package ├── ipfs_effector_config.json ├── ipfs_pure_config.json ├── on_start.air ├── on_start.json └── package.sh ├── example ├── README.md ├── aqua │ └── export.aqua ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json ├── local-network └── docker-compose.yml ├── rust-toolchain.toml └── service ├── Cargo.lock ├── Cargo.toml ├── artifacts └── Config.toml ├── build.sh ├── distro ├── Cargo.toml ├── build.rs └── src │ └── lib.rs ├── effector ├── Cargo.toml ├── build.rs ├── src │ ├── effector.rs │ └── main.rs └── tests │ ├── Config.toml │ ├── Config_error.toml │ ├── Config_put.toml │ ├── ipfs │ ├── ipfs_error │ ├── ipfs_put │ └── tests.rs ├── pure ├── Cargo.toml ├── build.rs ├── src │ ├── main.rs │ └── pure.rs └── tests │ ├── Config.toml │ └── tests.rs └── types ├── Cargo.toml └── src ├── lib.rs └── results.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [registries] 2 | fluence = { index = "git://crates.fluence.dev/index" } 3 | -------------------------------------------------------------------------------- /.github/actionlint.yaml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | labels: 3 | - builder 4 | -------------------------------------------------------------------------------- /.github/e2e/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nox: 3 | image: ${NOX_IMAGE:-fluencelabs/nox:unstable_minimal} 4 | ports: 5 | - 7771:7771 6 | - 9991:9991 7 | environment: 8 | - FLUENCE_ENV_AQUA_IPFS_EXTERNAL_API_MULTIADDR=/ip4/127.0.0.1/tcp/5001 9 | - FLUENCE_ENV_AQUA_IPFS_LOCAL_API_MULTIADDR=/dns4/ipfs/tcp/5001 10 | command: 11 | - --aqua-pool-size=2 12 | - -t=7771 13 | - -w=9991 14 | - --local 15 | # 12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR 16 | - -k=hK62afickoeP2uZbmSkAYXxxqP8ozq16VRN7qfTP719EHC5V5tjrtW57BSjUr8GvsEXmJRbtejUWyPZ2rZMyQdq 17 | 18 | ipfs: 19 | image: ipfs/kubo:latest 20 | ports: 21 | - 5001:5001 22 | volumes: 23 | - ./ipfs.sh:/container-init.d/01-ipfs.sh 24 | -------------------------------------------------------------------------------- /.github/e2e/ipfs.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]' 4 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "POST"]' 5 | ipfs config --json Addresses.API '"/ip4/0.0.0.0/tcp/5001"' 6 | ipfs bootstrap rm --all 7 | -------------------------------------------------------------------------------- /.github/release-please/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap-sha": "54d655a85bae41c14c5cf415799a142262c8c063", 3 | "release-type": "simple", 4 | "bump-minor-pre-major": true, 5 | "bump-patch-for-minor-pre-major": true, 6 | "packages": { 7 | ".": { 8 | "package-name": "aqua-ipfs", 9 | "component": "aqua-ipfs" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/release-please/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "0.6.0" 3 | } 4 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>fluencelabs/renovate", 4 | "github>fluencelabs/renovate:npm", 5 | "github>fluencelabs/renovate:cargo" 6 | ], 7 | "enabledManagers": ["cargo", "npm", "pip_requirements"] 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: "e2e" 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - "**.md" 7 | - ".github/**" 8 | - "!.github/workflows/e2e.yml" 9 | - "!.github/workflows/snapshot.yml" 10 | types: 11 | - "labeled" 12 | - "synchronize" 13 | - "opened" 14 | - "reopened" 15 | push: 16 | branches: 17 | - "main" 18 | paths-ignore: 19 | - "**.md" 20 | - ".github/**" 21 | - "!.github/workflows/e2e.yml" 22 | - "!.github/workflows/snapshot.yml" 23 | 24 | concurrency: 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | snapshot: 30 | if: > 31 | github.event_name == 'push' || 32 | contains(github.event.pull_request.labels.*.name, 'e2e') 33 | name: "aqua-ipfs" 34 | uses: ./.github/workflows/snapshot.yml 35 | with: 36 | ref: ${{ github.ref }} 37 | 38 | nox: 39 | needs: 40 | - snapshot 41 | uses: fluencelabs/nox/.github/workflows/build.yml@master 42 | with: 43 | ref: new-vault 44 | cargo-dependencies: | 45 | [ 46 | { 47 | "package": "aqua-ipfs-distro", 48 | "version": "=${{ needs.snapshot.outputs.cargo-version }}", 49 | "manifest": "crates/system-services/Cargo.toml", 50 | "registry": "fluence" 51 | } 52 | ] 53 | 54 | nox-snapshot: 55 | name: "nox" 56 | needs: 57 | - nox 58 | 59 | uses: fluencelabs/nox/.github/workflows/container.yml@master 60 | with: 61 | image-name: "docker.fluence.dev/aqua-ipfs" 62 | flavour: "minimal" 63 | nox-sha: "${{ needs.nox.outputs.nox-sha }}" 64 | 65 | aqua-ipfs-tests: 66 | name: "aqua-ipfs" 67 | needs: 68 | - nox-snapshot 69 | uses: ./.github/workflows/tests.yml 70 | with: 71 | ref: ${{ github.ref }} 72 | nox-image: "${{ needs.nox-snapshot.outputs.nox-image }}" 73 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | concurrency: 11 | group: "${{ github.workflow }}-${{ github.ref }}" 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | pr: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: amannn/action-semantic-pull-request@v5 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | reviewdog: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Lint actions 30 | uses: reviewdog/action-actionlint@v1 31 | env: 32 | SHELLCHECK_OPTS: "-e SC2086 -e SC2207 -e SC2128" 33 | with: 34 | reporter: github-pr-check 35 | fail_on_error: true 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "release" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | 8 | concurrency: 9 | group: "${{ github.workflow }}-${{ github.ref }}" 10 | 11 | jobs: 12 | release-please: 13 | runs-on: ubuntu-latest 14 | 15 | outputs: 16 | release-created: ${{ steps.release.outputs['release_created'] }} 17 | tag-name: ${{ steps.release.outputs['tag_name'] }} 18 | version: ${{ steps.release.outputs['version'] }} 19 | pr: ${{ steps.release.outputs['pr'] }} 20 | 21 | steps: 22 | - name: Run release-please 23 | id: release 24 | uses: google-github-actions/release-please-action@v3 25 | with: 26 | token: ${{ secrets.FLUENCEBOT_RELEASE_PLEASE_PAT }} 27 | command: manifest 28 | config-file: .github/release-please/config.json 29 | manifest-file: .github/release-please/manifest.json 30 | 31 | - name: Show output from release-please 32 | if: steps.release.outputs.releases_created 33 | env: 34 | RELEASE_PLEASE_OUTPUT: ${{ toJSON(steps.release.outputs) }} 35 | run: echo "${RELEASE_PLEASE_OUTPUT}" | jq 36 | 37 | bump-version: 38 | if: needs.release-please.outputs.pr != null 39 | runs-on: builder 40 | needs: 41 | - release-please 42 | 43 | permissions: 44 | contents: write 45 | 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v3 49 | with: 50 | ref: ${{ fromJson(needs.release-please.outputs.pr).headBranchName }} 51 | 52 | - name: Get version 53 | id: version 54 | run: | 55 | version="$(jq -r '.[]' .github/release-please/manifest.json)" 56 | echo "version=${version}" >> $GITHUB_OUTPUT 57 | 58 | - name: Setup Rust toolchain 59 | uses: actions-rust-lang/setup-rust-toolchain@v1 60 | 61 | - name: Install cargo-edit 62 | uses: baptiste0928/cargo-install@v2.0.0 63 | with: 64 | crate: cargo-edit 65 | version: v0.12.1 66 | 67 | - name: Set version in service 68 | working-directory: service 69 | run: cargo set-version ${{ steps.version.outputs.version }} 70 | 71 | - name: Setup node 72 | uses: actions/setup-node@v3 73 | with: 74 | node-version: "18" 75 | registry-url: "https://registry.npmjs.org" 76 | cache-dependency-path: "aqua/package-lock.json" 77 | cache: "npm" 78 | 79 | - name: Set version in aqua 80 | working-directory: aqua 81 | run: npm version ${{ steps.version.outputs.version }} 82 | 83 | - name: Commit version bump 84 | uses: stefanzweifel/git-auto-commit-action@v4 85 | with: 86 | commit_message: "chore: Bump aqua-ipfs version to ${{ steps.version.outputs.version }}" 87 | branch: ${{ fromJson(needs.release-please.outputs.pr).headBranchName }} 88 | commit_user_name: fluencebot 89 | commit_user_email: devops@fluence.one 90 | commit_author: fluencebot 91 | 92 | aqua-ipfs: 93 | runs-on: builder 94 | 95 | needs: release-please 96 | if: needs.release-please.outputs.release-created 97 | 98 | permissions: 99 | contents: write 100 | id-token: write 101 | 102 | steps: 103 | - name: Checkout 104 | uses: actions/checkout@v3 105 | 106 | - name: Import secrets 107 | uses: hashicorp/vault-action@v2.4.3 108 | with: 109 | url: https://vault.fluence.dev 110 | path: jwt/github 111 | role: ci 112 | method: jwt 113 | jwtGithubAudience: "https://github.com/fluencelabs" 114 | jwtTtl: 300 115 | exportToken: false 116 | secrets: | 117 | kv/npmjs/fluencebot token | NODE_AUTH_TOKEN ; 118 | kv/crates.io/fluencebot token | CARGO_REGISTRY_TOKEN 119 | 120 | - name: Setup Rust toolchain 121 | uses: actions-rust-lang/setup-rust-toolchain@v1 122 | 123 | - name: Install cargo-workspaces 124 | uses: baptiste0928/cargo-install@v2.0.0 125 | with: 126 | crate: cargo-workspaces 127 | version: v0.2.44 128 | 129 | - name: Setup marine 130 | uses: fluencelabs/setup-marine@v1 131 | 132 | - name: Build service 133 | run: ./build.sh 134 | working-directory: service 135 | 136 | - name: Setup node 137 | uses: actions/setup-node@v3 138 | with: 139 | node-version: "18" 140 | registry-url: "https://registry.npmjs.org" 141 | cache-dependency-path: "aqua/package-lock.json" 142 | cache: "npm" 143 | 144 | - name: Setup fcli 145 | uses: fluencelabs/setup-fluence@v1 146 | with: 147 | version: stable 148 | 149 | - run: npm ci 150 | working-directory: aqua 151 | 152 | - run: npm run build 153 | working-directory: aqua 154 | 155 | - name: Publish to NPM registry 156 | run: npm publish --access public 157 | working-directory: aqua 158 | continue-on-error: true 159 | 160 | - name: Publish to crates.io 161 | working-directory: ./service 162 | continue-on-error: true 163 | run: | 164 | cargo ws publish \ 165 | --no-git-commit \ 166 | --allow-dirty \ 167 | --from-git \ 168 | --skip-published \ 169 | --yes 170 | 171 | - name: Install ipfs 172 | uses: nahsi/setup-ipfs@v1 173 | 174 | - name: Create builtin distribution package 175 | run: ./builtin-package/package.sh 176 | 177 | - name: Calculate SHA256 178 | id: sha 179 | run: | 180 | # Calculate sha256 181 | du -hs aqua-ipfs.tar.gz 182 | sha256sum aqua-ipfs.tar.gz 183 | sha=($(sha256sum aqua-ipfs.tar.gz)) 184 | echo "sha256=${sha}" >> $GITHUB_OUTPUT 185 | 186 | - name: Upload aqua-ipfs package 187 | if: needs.release-please.outputs.release-created 188 | uses: softprops/action-gh-release@v1 189 | with: 190 | files: aqua-ipfs.tar.gz 191 | tag_name: ${{ needs.release-please.outputs.tag-name }} 192 | 193 | slack: 194 | if: always() 195 | name: "Notify" 196 | runs-on: ubuntu-latest 197 | 198 | needs: 199 | - release-please 200 | - aqua-ipfs 201 | 202 | permissions: 203 | contents: read 204 | id-token: write 205 | 206 | steps: 207 | - uses: lwhiteley/dependent-jobs-result-check@v1 208 | id: status 209 | with: 210 | statuses: failure 211 | dependencies: ${{ toJSON(needs) }} 212 | 213 | - name: Log output 214 | run: | 215 | echo "statuses:" "${{ steps.status.outputs.statuses }}" 216 | echo "jobs:" "${{ steps.status.outputs.jobs }}" 217 | echo "found any?:" "${{ steps.status.outputs.found }}" 218 | 219 | - name: Import secrets 220 | uses: hashicorp/vault-action@v2.4.3 221 | with: 222 | url: https://vault.fluence.dev 223 | path: jwt/github 224 | role: ci 225 | method: jwt 226 | jwtGithubAudience: "https://github.com/fluencelabs" 227 | jwtTtl: 300 228 | exportToken: false 229 | secrets: | 230 | kv/slack/release-please webhook | SLACK_WEBHOOK_URL 231 | 232 | - uses: ravsamhq/notify-slack-action@v2 233 | if: steps.status.outputs.found == 'true' 234 | with: 235 | status: "failure" 236 | notification_title: "*{workflow}* has {status_message}" 237 | message_format: "${{ steps.status.outputs.jobs }} {status_message} in <{repo_url}|{repo}>" 238 | footer: "<{run_url}>" 239 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - "**.md" 7 | 8 | push: 9 | branches: 10 | - "master" 11 | 12 | concurrency: 13 | group: "${{ github.workflow }}-${{ github.ref }}" 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | tests: 18 | name: "cargo nextest" 19 | runs-on: builder 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Setup Rust toolchain 26 | uses: actions-rust-lang/setup-rust-toolchain@v1 27 | 28 | - name: Setup marine 29 | uses: fluencelabs/setup-marine@v1 30 | 31 | - name: Install cargo-nextest 32 | uses: baptiste0928/cargo-install@v2.0.0 33 | with: 34 | crate: cargo-nextest 35 | version: 0.9.22 36 | 37 | - name: Build aqua-ipfs 38 | working-directory: ./service 39 | run: ./build.sh 40 | 41 | - name: Run cargo nextest 42 | working-directory: ./service 43 | env: 44 | NEXTEST_TEST_THREADS: 10 45 | run: cargo nextest run --release --all-features --no-fail-fast 46 | 47 | - name: Run cargo clippy 48 | uses: actions-rs/cargo@v1 49 | with: 50 | command: clippy 51 | args: -Z unstable-options --all --manifest-path service/Cargo.toml 52 | 53 | npm-build: 54 | name: "Check if npm run build works" 55 | runs-on: ubuntu-latest 56 | 57 | steps: 58 | - name: Checkout sources 59 | uses: actions/checkout@v3 60 | 61 | - name: Setup node 62 | uses: actions/setup-node@v3 63 | with: 64 | node-version: "18" 65 | registry-url: "https://registry.npmjs.org" 66 | # cache-dependency-path: "aqua/package-lock.json" 67 | # cache: "npm" 68 | 69 | - name: Setup fcli 70 | uses: fluencelabs/setup-fluence@v1 71 | with: 72 | version: stable 73 | 74 | - run: npm ci 75 | working-directory: aqua 76 | 77 | - run: npm run build 78 | working-directory: aqua 79 | 80 | 81 | lints: 82 | name: Lints 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Checkout sources 87 | uses: actions/checkout@v3 88 | 89 | - name: Setup rust toolchain 90 | uses: actions-rust-lang/setup-rust-toolchain@v1 91 | 92 | - name: Run cargo fmt 93 | uses: actions-rs/cargo@v1 94 | with: 95 | command: fmt 96 | args: --all --manifest-path service/Cargo.toml -- --check 97 | -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Build snapshot 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | cargo-dependencies: 7 | description: "Cargo dependencies map" 8 | type: string 9 | default: "null" 10 | ref: 11 | description: "git ref to checkout to" 12 | type: string 13 | default: "main" 14 | outputs: 15 | cargo-version: 16 | description: "Cargo snapshot version" 17 | value: ${{ jobs.snapshot.outputs.version }} 18 | 19 | jobs: 20 | snapshot: 21 | name: "Build and test crates" 22 | runs-on: builder 23 | timeout-minutes: 60 24 | 25 | outputs: 26 | version: "${{ steps.snapshot.outputs.version }}" 27 | 28 | permissions: 29 | contents: read 30 | id-token: write 31 | 32 | steps: 33 | - uses: actions/checkout@v3 34 | with: 35 | repository: fluencelabs/aqua-ipfs 36 | ref: ${{ inputs.ref }} 37 | 38 | - name: Setup rust toolchain 39 | uses: actions-rust-lang/setup-rust-toolchain@v1 40 | with: 41 | cache: false 42 | 43 | - name: Set dependencies 44 | if: inputs.cargo-dependencies != 'null' 45 | uses: fluencelabs/github-actions/cargo-set-dependency@main 46 | with: 47 | dependencies: ${{ inputs.cargo-dependencies }} 48 | path: service 49 | 50 | - name: Setup marine 51 | uses: fluencelabs/setup-marine@v1 52 | with: 53 | artifact-name: marine 54 | 55 | - name: Build and package aqua-ipfs 56 | working-directory: ./service 57 | run: ./build.sh 58 | 59 | - name: Import secrets 60 | uses: hashicorp/vault-action@v2.5.0 61 | with: 62 | url: https://vault.fluence.dev 63 | path: jwt/github 64 | role: ci 65 | method: jwt 66 | jwtGithubAudience: "https://github.com/fluencelabs" 67 | jwtTtl: 300 68 | exportToken: false 69 | secrets: | 70 | kv/cargo-registry/users/ci token | CARGO_REGISTRIES_FLUENCE_TOKEN 71 | 72 | - name: Generate snapshot version 73 | id: version 74 | uses: fluencelabs/github-actions/generate-snapshot-id@main 75 | 76 | - name: Publish crate snapshots 77 | id: snapshot 78 | uses: fluencelabs/github-actions/cargo-publish-snapshot@main 79 | with: 80 | id: ${{ steps.version.outputs.id }} 81 | path: service/distro 82 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests with workflow_call 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | fluence-env: 7 | description: "Fluence enviroment to run tests agains" 8 | type: string 9 | default: "local" 10 | nox-image: 11 | description: "nox image tag" 12 | type: string 13 | default: "fluencelabs/nox:unstable_minimal" 14 | ref: 15 | description: "GitHub ref to checkout to" 16 | type: string 17 | default: "main" 18 | flox-version: 19 | description: "@fluencelabs/flox version" 20 | type: string 21 | default: "null" 22 | 23 | env: 24 | CI: true 25 | FORCE_COLOR: true 26 | NOX_IMAGE: "${{ inputs.nox-image }}" 27 | FLUENCE_ENV: "${{ inputs.fluence-env }}" 28 | 29 | jobs: 30 | aqua-ipfs: 31 | name: "Run tests" 32 | runs-on: builder 33 | 34 | permissions: 35 | contents: read 36 | id-token: write 37 | 38 | steps: 39 | - name: Import secrets 40 | uses: hashicorp/vault-action@v2.4.3 41 | with: 42 | url: https://vault.fluence.dev 43 | path: jwt/github 44 | role: ci 45 | method: jwt 46 | jwtGithubAudience: "https://github.com/fluencelabs" 47 | jwtTtl: 300 48 | secrets: | 49 | kv/docker-registry/basicauth/ci username | DOCKER_USERNAME ; 50 | kv/docker-registry/basicauth/ci password | DOCKER_PASSWORD 51 | 52 | - name: Checkout aqua-ipfs 53 | uses: actions/checkout@v3 54 | with: 55 | repository: fluencelabs/aqua-ipfs 56 | ref: ${{ inputs.ref }} 57 | 58 | - name: Setup Rust toolchain 59 | uses: actions-rust-lang/setup-rust-toolchain@v1 60 | 61 | - name: Setup marine 62 | uses: fluencelabs/setup-marine@v1 63 | with: 64 | artifact-name: marine 65 | 66 | - name: Build aqua-ipfs 67 | working-directory: ./service 68 | run: ./build.sh 69 | 70 | - name: Setup node with self-hosted registry 71 | uses: actions/setup-node@v3 72 | with: 73 | node-version: "18" 74 | registry-url: "https://npm.fluence.dev" 75 | cache: "npm" 76 | cache-dependency-path: "aqua-tests/package-lock.json" 77 | 78 | - run: npm install 79 | working-directory: aqua-tests 80 | 81 | - name: Set flox version 82 | if: inputs.flox-version != 'null' 83 | uses: fluencelabs/github-actions/npm-set-dependency@main 84 | with: 85 | package: "@fluencelabs/cli" 86 | version: ${{ inputs.flox-version }} 87 | working-directory: aqua-tests 88 | flags: "--save-dev" 89 | 90 | - name: Login to DockerHub 91 | uses: docker/login-action@v2 92 | with: 93 | registry: docker.fluence.dev 94 | username: ${{ env.DOCKER_USERNAME }} 95 | password: ${{ env.DOCKER_PASSWORD }} 96 | 97 | - name: Pull nox image 98 | run: docker pull $NOX_IMAGE 99 | 100 | - name: Run nox network 101 | uses: isbang/compose-action@v1.4.1 102 | with: 103 | compose-file: ".github/e2e/docker-compose.yml" 104 | down-flags: "--volumes" 105 | 106 | - name: Setup python 107 | uses: actions/setup-python@v4 108 | with: 109 | python-version: "3.9" 110 | cache: "pip" 111 | cache-dependency-path: aqua-tests/requirements.txt 112 | 113 | - name: Install python requirements 114 | run: pip install -r requirements.txt 115 | working-directory: aqua-tests 116 | 117 | - name: Install fluence deps 118 | env: 119 | FLUENCE_USER_DIR: "${{ github.workspace }}/tmp/.fluence" 120 | run: npx fluence dep install 121 | working-directory: aqua-tests 122 | 123 | - name: Install ipfs 124 | uses: nahsi/setup-ipfs@v1 125 | 126 | - name: Run aqua tests 127 | env: 128 | FLUENCE_USER_DIR: "${{ github.workspace }}/tmp/.fluence" 129 | run: pytest -n auto test_aqua.py 130 | working-directory: aqua-tests 131 | 132 | - name: Print versions to check summary 133 | if: always() 134 | working-directory: aqua-tests 135 | run: | 136 | cat <> $GITHUB_STEP_SUMMARY 137 | ## Used versions 138 | \`\`\` 139 | $(npx fluence dep v) 140 | \`\`\` 141 | SNAPSHOT 142 | 143 | - name: Dump nox logs 144 | if: always() 145 | uses: jwalton/gh-docker-logs@v2 146 | 147 | - name: Remove tmp directory 148 | if: always() 149 | run: sudo rm -rf tmp 150 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | service/**/target/ 4 | 5 | aqua/target/typescript/** 6 | example/generated/** 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | 11 | # Added by cargo 12 | .idea 13 | 14 | service/artifacts/* 15 | !service/artifacts/Config.toml 16 | builtin-package/*.wasm 17 | 18 | **/node_modules 19 | **/dist 20 | .DS_store 21 | .bic_cache 22 | 23 | aqua-ipfs.tar.gz 24 | **/ipfs-service/* 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.6.0](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.31...aqua-ipfs-v0.6.0) (2024-02-22) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Support CallParameters and Particle Vault File Format ([#165](https://github.com/fluencelabs/aqua-ipfs/issues/165)) 9 | 10 | ### Features 11 | 12 | * Support CallParameters and Particle Vault File Format ([#165](https://github.com/fluencelabs/aqua-ipfs/issues/165)) ([e3e89cf](https://github.com/fluencelabs/aqua-ipfs/commit/e3e89cf4d0855712ad181894c0cf5668f8798ca5)) 13 | 14 | ## [0.5.31](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.30...aqua-ipfs-v0.5.31) (2024-02-21) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * **aqua-lib:** update aqua-lib ([#166](https://github.com/fluencelabs/aqua-ipfs/issues/166)) ([41fbc08](https://github.com/fluencelabs/aqua-ipfs/commit/41fbc08d11e0974b93a7c27209a493516c4fb8a8)) 20 | 21 | ## [0.5.30](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.29...aqua-ipfs-v0.5.30) (2024-01-11) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * **aqua-ipfs:** Update example ([#159](https://github.com/fluencelabs/aqua-ipfs/issues/159)) ([32238f1](https://github.com/fluencelabs/aqua-ipfs/commit/32238f18c2df5edfe39a43c051bf4f08d76721f4)) 27 | 28 | ## [0.5.29](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.28...aqua-ipfs-v0.5.29) (2024-01-04) 29 | 30 | 31 | ### Features 32 | 33 | * **aqua-ipfs:** Use `aqua` keyword instead of `module` ([#153](https://github.com/fluencelabs/aqua-ipfs/issues/153)) ([d973633](https://github.com/fluencelabs/aqua-ipfs/commit/d9736332dcbe7b2389c650157d2c8a84b7386939)) 34 | 35 | ## [0.5.28](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.27...aqua-ipfs-v0.5.28) (2023-12-27) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * **deps:** update dependency @fluencelabs/aqua-lib to v0.9.0 ([#139](https://github.com/fluencelabs/aqua-ipfs/issues/139)) ([e5e1e17](https://github.com/fluencelabs/aqua-ipfs/commit/e5e1e170dd8405cc720998e8522969e9248c73fb)) 41 | * **deps:** update dependency @fluencelabs/fluence-network-environment to v1.1.2 ([#113](https://github.com/fluencelabs/aqua-ipfs/issues/113)) ([f184b1f](https://github.com/fluencelabs/aqua-ipfs/commit/f184b1fff68e84a7e987fa0afe56fb231ed62e6d)) 42 | * **deps:** update rust crate marine-rs-sdk-test to 0.12.1 ([#137](https://github.com/fluencelabs/aqua-ipfs/issues/137)) ([b98b9b1](https://github.com/fluencelabs/aqua-ipfs/commit/b98b9b17e28ac61578d4abf5cf5489bfe2a17541)) 43 | 44 | ## [0.5.27](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.26...aqua-ipfs-v0.5.27) (2023-12-26) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * add memory limit into distro config ([#135](https://github.com/fluencelabs/aqua-ipfs/issues/135)) ([9994067](https://github.com/fluencelabs/aqua-ipfs/commit/999406708f958374e6ce43673f550e18bf651d80)) 50 | 51 | ## [0.5.26](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.25...aqua-ipfs-v0.5.26) (2023-12-25) 52 | 53 | 54 | ### Features 55 | 56 | * update marine sdk's and configs ([#122](https://github.com/fluencelabs/aqua-ipfs/issues/122)) ([6db2393](https://github.com/fluencelabs/aqua-ipfs/commit/6db239348299451d5c3f3a2687fe53fae6b922c7)) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * **build:** fix npm build, add check for that ([#132](https://github.com/fluencelabs/aqua-ipfs/issues/132)) ([e6749d1](https://github.com/fluencelabs/aqua-ipfs/commit/e6749d1f05b1ad4ebf3aa4c3c166758e3288635e)) 62 | * **deps:** rust toolchain 2023-12-06; cargo-workspaces 0.2.44 ([#126](https://github.com/fluencelabs/aqua-ipfs/issues/126)) ([f096a4e](https://github.com/fluencelabs/aqua-ipfs/commit/f096a4efb46d5c24513745c7bea376e88fea9631)) 63 | * **release:** use fcli installer ([#134](https://github.com/fluencelabs/aqua-ipfs/issues/134)) ([812fa12](https://github.com/fluencelabs/aqua-ipfs/commit/812fa12506edca84c12a14dc1547c01182297d56)) 64 | 65 | ## [0.5.25](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.24...aqua-ipfs-v0.5.25) (2023-12-20) 66 | 67 | 68 | ### Bug Fixes 69 | 70 | * tests ([1d55067](https://github.com/fluencelabs/aqua-ipfs/commit/1d550679653864d5ea8d5951113f258c415a0527)) 71 | * **tests:** update package-lock to fix marine-js not found on fluence run ([#123](https://github.com/fluencelabs/aqua-ipfs/issues/123)) ([1d55067](https://github.com/fluencelabs/aqua-ipfs/commit/1d550679653864d5ea8d5951113f258c415a0527)) 72 | 73 | ## [0.5.24](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.23...aqua-ipfs-v0.5.24) (2023-11-15) 74 | 75 | 76 | ### Features 77 | 78 | * update toolchain and deps ([#116](https://github.com/fluencelabs/aqua-ipfs/issues/116)) ([b99d790](https://github.com/fluencelabs/aqua-ipfs/commit/b99d790d71f452e3eb00eda39ac3d9cada7f7fb8)) 79 | 80 | ## [0.5.23](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.22...aqua-ipfs-v0.5.23) (2023-11-15) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * **deps:** update dependency @fluencelabs/aqua-lib to v0.8.1 ([#114](https://github.com/fluencelabs/aqua-ipfs/issues/114)) ([ddc94ce](https://github.com/fluencelabs/aqua-ipfs/commit/ddc94cea48560d9d3ec79158b126b216eb1a0b29)) 86 | 87 | ## [0.5.22](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.21...aqua-ipfs-v0.5.22) (2023-11-15) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * **deps:** update dependency @fluencelabs/aqua-lib to v0.8.0 ([#57](https://github.com/fluencelabs/aqua-ipfs/issues/57)) ([fba3f93](https://github.com/fluencelabs/aqua-ipfs/commit/fba3f93ac49359220d19139579435bc00c402d8a)) 93 | 94 | ## [0.5.21](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.20...aqua-ipfs-v0.5.21) (2023-09-28) 95 | 96 | 97 | ### Features 98 | 99 | * **aqua-ipfs:** update aqua-lib to 0.7.7 ([#107](https://github.com/fluencelabs/aqua-ipfs/issues/107)) ([49dd30e](https://github.com/fluencelabs/aqua-ipfs/commit/49dd30eec395a416c11a52150409e0402b645365)) 100 | 101 | ## [0.5.20](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.19...aqua-ipfs-v0.5.20) (2023-09-22) 102 | 103 | 104 | ### Bug Fixes 105 | 106 | * bump distro version ([#105](https://github.com/fluencelabs/aqua-ipfs/issues/105)) ([0c48bec](https://github.com/fluencelabs/aqua-ipfs/commit/0c48bec52ab5cff3259a3f85ec381b5668d954f1)) 107 | 108 | ## [0.5.19](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.18...aqua-ipfs-v0.5.19) (2023-09-22) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * **ci:** bump cargo-install version ([9ed3419](https://github.com/fluencelabs/aqua-ipfs/commit/9ed34194fe8515b69f50dfb557ecb9f67c62bb92)) 114 | 115 | ## [0.5.18](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.17...aqua-ipfs-v0.5.18) (2023-09-22) 116 | 117 | 118 | ### Features 119 | 120 | * allow reconfigure multiaddrs ([#103](https://github.com/fluencelabs/aqua-ipfs/issues/103)) ([d620b14](https://github.com/fluencelabs/aqua-ipfs/commit/d620b148aa5902090b63971f4ddcd3c4adb2183a)) 121 | * support ipfs dag + e2e tests [NET-476] ([#91](https://github.com/fluencelabs/aqua-ipfs/issues/91)) ([1dd30ba](https://github.com/fluencelabs/aqua-ipfs/commit/1dd30ba0ace5f35de7a8db5c71d56c7e167a4de6)) 122 | 123 | ## [0.5.17](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.16...aqua-ipfs-v0.5.17) (2023-07-10) 124 | 125 | 126 | ### Bug Fixes 127 | 128 | * **ci:** fix compilation for release ([#98](https://github.com/fluencelabs/aqua-ipfs/issues/98)) ([24f68a0](https://github.com/fluencelabs/aqua-ipfs/commit/24f68a054c580e9144e5a434c739fdaa503674eb)) 129 | 130 | ## [0.5.16](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.15...aqua-ipfs-v0.5.16) (2023-07-06) 131 | 132 | 133 | ### Bug Fixes 134 | 135 | * **ci:** use node 18 ([#96](https://github.com/fluencelabs/aqua-ipfs/issues/96)) ([513c2cf](https://github.com/fluencelabs/aqua-ipfs/commit/513c2cfc3f6a9aa92f0d4bdad39568744e28f481)) 136 | 137 | ## [0.5.15](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.14...aqua-ipfs-v0.5.15) (2023-07-05) 138 | 139 | 140 | ### Bug Fixes 141 | 142 | * **aqua:** compile aqua with flox and fix for new aqua ([#95](https://github.com/fluencelabs/aqua-ipfs/issues/95)) ([68a2ca7](https://github.com/fluencelabs/aqua-ipfs/commit/68a2ca7a54f227426f8c8c5932c9b86c1b9a2cf5)) 143 | * **ci:** use unstable nox image ([#93](https://github.com/fluencelabs/aqua-ipfs/issues/93)) ([77ebe81](https://github.com/fluencelabs/aqua-ipfs/commit/77ebe8179a08f99c9593d7d1305551355491e060)) 144 | 145 | ## [0.5.14](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.13...aqua-ipfs-v0.5.14) (2023-06-09) 146 | 147 | 148 | ### Bug Fixes 149 | 150 | * distro crate config and modules names ([#82](https://github.com/fluencelabs/aqua-ipfs/issues/82)) ([cfecf86](https://github.com/fluencelabs/aqua-ipfs/commit/cfecf86ab59cf1fad5e880240705ec5bb0a09c6e)) 151 | 152 | ## [0.5.13](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.12...aqua-ipfs-v0.5.13) (2023-05-08) 153 | 154 | 155 | ### Features 156 | 157 | * **builtin-package:** use new blueprint ([#81](https://github.com/fluencelabs/aqua-ipfs/issues/81)) ([4ce8e8b](https://github.com/fluencelabs/aqua-ipfs/commit/4ce8e8bce7f9d0b4ea046b73391f3b5199ab02a6)) 158 | 159 | ## [0.5.12](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.11...aqua-ipfs-v0.5.12) (2023-05-03) 160 | 161 | 162 | ### Features 163 | 164 | * **release:** continue if publishing fails ([#78](https://github.com/fluencelabs/aqua-ipfs/issues/78)) ([ceb8805](https://github.com/fluencelabs/aqua-ipfs/commit/ceb88050f40292fa49eb195d31f25e254d9f426b)) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * **ci:** install cargo-workspaces with cargo-install ([c8de5e2](https://github.com/fluencelabs/aqua-ipfs/commit/c8de5e2f7dd0f1d9b92c6df7c659780432e94ce5)) 170 | * **config:** set default timeout to 10s ([#75](https://github.com/fluencelabs/aqua-ipfs/issues/75)) ([8030d4d](https://github.com/fluencelabs/aqua-ipfs/commit/8030d4d306dd800435d006ddc7a81ad286a3c859)) 171 | 172 | ## [0.5.11](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.10...aqua-ipfs-v0.5.11) (2023-04-24) 173 | 174 | 175 | ### Features 176 | 177 | * add aqua-ipfs-distro crate ([#72](https://github.com/fluencelabs/aqua-ipfs/issues/72)) ([01c645e](https://github.com/fluencelabs/aqua-ipfs/commit/01c645e8c4e302552e0dfb065099d9f5d5807ce3)) 178 | 179 | ## [0.5.10](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.9...aqua-ipfs-v0.5.10) (2023-04-12) 180 | 181 | 182 | ### Bug Fixes 183 | 184 | * **deps:** update aqua-lib to 0.7.0 and aqua to 0.10.3 ([#70](https://github.com/fluencelabs/aqua-ipfs/issues/70)) ([a4403e1](https://github.com/fluencelabs/aqua-ipfs/commit/a4403e1e64d2ee3a3ef2483a8367b0b8e3991d87)) 185 | 186 | ## [0.5.9](https://github.com/fluencelabs/aqua-ipfs/compare/aqua-ipfs-v0.5.8...aqua-ipfs-v0.5.9) (2023-02-08) 187 | 188 | 189 | ### Features 190 | 191 | * **api:** implement 'ipfs cat' ([#48](https://github.com/fluencelabs/aqua-ipfs/issues/48)) ([d641e0e](https://github.com/fluencelabs/aqua-ipfs/commit/d641e0e29cc2fb46b10091dd76215895942f4994)) 192 | 193 | 194 | ### Bug Fixes 195 | 196 | * **ci:** set cargo and npm ([#51](https://github.com/fluencelabs/aqua-ipfs/issues/51)) ([aa934f2](https://github.com/fluencelabs/aqua-ipfs/commit/aa934f29739b0aadcddf19d05848c01d3bf7385b)) 197 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contribute Code 2 | 3 | You are welcome to contribute to Fluence! 4 | 5 | Things you need to know: 6 | 7 | 1. You need to **agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7) (CLA)**. This is a common practice in all major Open Source projects. At the current moment, we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted. 8 | 9 | 2. **Not all proposed contributions can be accepted**. Some features may, e.g., just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make. 10 | 11 | ### Contributor License Agreement 12 | 13 | When you contribute, you have to be aware that your contribution is covered by **[Apache License 2.0](./LICENSE)**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**. In particular, you need to agree to the Contributor License Agreement. If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. The CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aqua IPFS bindings 2 | 3 | [![npm](https://img.shields.io/npm/v/@fluencelabs/aqua-ipfs)](https://www.npmjs.com/package/@fluencelabs/aqua-ipfs) 4 | 5 | Aqua-ipfs is a native [IPFS](https://ipfs.tech/) integration to [Aqua](https://fluence.dev/docs/aqua-book/introduction) language. It lets one call the API of an IPFS daemon, e.g., to transfer files between peers & services or to orchestrate IPFS nodes. 6 | 7 | ## Quick Installation and Usage 8 | 9 | First, make sure you have [Fluence CLI](https://github.com/fluencelabs/fluence-cli) installed. After this, installation is as simple as: 10 | 11 | ``` 12 | fluence dep npm i @fluencelabs/aqua-ipfs 13 | ``` 14 | 15 | Next, run: 16 | 17 | ``` 18 | import "@fluencelabs/aqua-ipfs/ipfs-api.aqua" 19 | import "@fluencelabs/aqua-lib/builtin.aqua" 20 | 21 | const PERIOD_SEC = 10 22 | 23 | func install_scheduled_script_from_ipfs_to_peer(from: PeerId, to: PeerId, cid: string) -> ?string: 24 | script_id: ?string 25 | 26 | ipfs_maddr <- get_external_api_multiaddr(from) 27 | if ipfs_maddr.success: 28 | get <- get_from(to, cid, ipfs_maddr.multiaddr) 29 | if get.success: 30 | script_id <- Script.add_from_vault(get.path, ?[PERIOD_SEC]) 31 | 32 | <- script_id 33 | ``` 34 | 35 | To run it in Marine REPL (mrepl), one needs to install `mrepl` via `cargo install mrepl` 36 | first. After that: 37 | 38 | ``` 39 | cd service 40 | mrepl 41 | new Config.toml 42 | call ipfs_pure set_local_api_multiaddr ["/ip4/134.209.186.43/tcp/5001/p2p/12D3KooWEhCqQ9NBnmtSfNeXSNfhgccmH86xodkCUxZNEXab6pkw"] 43 | 44 | call ipfs_pure set_external_api_multiaddr ["/ip4/134.209.186.43/tcp/5001/p2p/12D3KooWEhCqQ9NBnmtSfNeXSNfhgccmH86xodkCUxZNEXab6pkw"] 45 | 46 | call ipfs_pure get_from ["QmfBRabun4FpaHV4wVXtnqtopUTro93XJHiWhNZscViCaq", "/ip4/134.209.186.43/tcp/5001/p2p/12D3KooWEhCqQ9NBnmtSfNeXSNfhgccmH86xodkCUxZNEXab6pkw"] 47 | ``` 48 | 49 | You can use `interface` and `help` inside `mrepl` to further discover what's possible. 50 | 51 | A simple example of using `aqua-ipfs` in TypeScript is available [here](./example/index.ts). 52 | 53 | 54 | ## Documentation 55 | 56 | Comprehensive documentation including API and usage examples can be found in [Aqua Book](https://fluence.dev/docs/aqua-book/libraries/aqua-ipfs). 57 | 58 | 59 | ## Repository Structure 60 | 61 | - [**aqua**](./aqua) is Aqua API of Aqua-ipfs. See its [README](./aqua/README.md) for details. 62 | - [**builtin-package**](./builtin-package) Files necessary to use Aqua-ipfs on peers 63 | - [**example**](./example) A simple example of how to use IPFS adapter from TypeScript 64 | - [**local-network**](./local-network) contains Docker Compose YAML config to run a local Fluence network of 3 nodes 65 | - [**service**](./service) contains Rust service that implements all IPFS calls by using `ipfs` CLI mounted binary 66 | 67 | 68 | ## Support 69 | 70 | Please, file an [issue](https://github.com/fluencelabs/aqua-ipfs/issues) if you find a bug. You can also contact us at [Discord](https://discord.com/invite/5qSnPZKh7u) or [Telegram](https://t.me/fluence_project). We will do our best to resolve the issue ASAP. 71 | 72 | 73 | ## Contributing 74 | 75 | Any interested person is welcome to contribute to the project. Please, make sure you read and follow some basic [rules](./CONTRIBUTING.md). 76 | 77 | 78 | ## License 79 | 80 | All software code is copyright (c) Fluence Labs, Inc. under the [Apache-2.0](./LICENSE) license. 81 | 82 | -------------------------------------------------------------------------------- /aqua-tests/.fluence/secrets/test_put_get_dag.txt: -------------------------------------------------------------------------------- 1 | BNidntUryx+hxr7NK2z9nci23sMn3fURB6bTH1K2Ll4= -------------------------------------------------------------------------------- /aqua-tests/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | .fluence/* 4 | !.fluence/secrets 5 | **/node_modules 6 | **/target/ 7 | .repl_history 8 | .vscode 9 | src/ts/src/aqua 10 | src/js/src/aqua 11 | __pycache__ 12 | .pytest_cache 13 | -------------------------------------------------------------------------------- /aqua-tests/README.md: -------------------------------------------------------------------------------- 1 | # Aqua-IPFS API tests 2 | 3 | ## How to run 4 | 5 | - `npm i` 6 | - `pip3 install -r requirements.txt` 7 | - `pip install -U pytest` 8 | - `pytest -n auto` 9 | 10 | ## Adding new test 11 | 12 | Before adding new test go to the aqua-tests dir first, then run `npm run secret` 13 | to add a new key-pair for the new test. 14 | Name it the same way the test function will be called (e.g. `test_create_resource`) 15 | This is required for tests to run in parallel. Key-pairs could've been generated on the fly 16 | but it's a bit faster to not waste time on it each time the tests are run 17 | -------------------------------------------------------------------------------- /aqua-tests/config.py: -------------------------------------------------------------------------------- 1 | 2 | def get_local(): 3 | return [ 4 | '/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR', 5 | ] 6 | -------------------------------------------------------------------------------- /aqua-tests/fluence.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=.fluence/schemas/fluence.json 2 | 3 | # Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use `fluence init` command to generate a template for new Fluence project 4 | 5 | # Documentation: https://github.com/fluencelabs/fluence-cli/tree/main/docs/configs/fluence.md 6 | 7 | version: 8 8 | 9 | aquaDependencies: 10 | "@fluencelabs/aqua-lib": 0.9.1 11 | "@fluencelabs/spell": 0.6.9 12 | 13 | compileAqua: 14 | default: 15 | input: src/aqua/main.aqua 16 | output: src/compiled-aqua 17 | target: ts 18 | -------------------------------------------------------------------------------- /aqua-tests/getDefaultPeers.js: -------------------------------------------------------------------------------- 1 | const { 2 | krasnodar, 3 | stage, 4 | testNet, 5 | } = require('@fluencelabs/fluence-network-environment') 6 | 7 | console.log( 8 | JSON.stringify({ 9 | krasnodar, 10 | stage, 11 | testnet: testNet, 12 | }), 13 | ) 14 | -------------------------------------------------------------------------------- /aqua-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aqua-tests", 3 | "version": "1.0.0", 4 | "description": "Aqua-IPFS tests", 5 | "dependencies": { 6 | "@fluencelabs/aqua-lib": "0.10.1", 7 | "@fluencelabs/aqua-ipfs": "file:../aqua" 8 | }, 9 | "scripts": { 10 | "compile-aqua": "fluence aqua -i . -o ./target/typescript", 11 | "generate-aqua": "../service/build.sh", 12 | "build": "npm run compile-aqua", 13 | "secret": "npx fluence key new" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/fluencelabs/aqua-ipfs.git", 18 | "directory": "aqua" 19 | }, 20 | "keywords": [ 21 | "aqua", 22 | "fluence" 23 | ], 24 | "author": "Fluence Labs", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/fluencelabs/aqua-ipfs/issues" 28 | }, 29 | "homepage": "https://github.com/fluencelabs/aqua-ipfs", 30 | "devDependencies": { 31 | "@fluencelabs/cli": "0.15.1", 32 | "@fluencelabs/fluence-network-environment": "1.1.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /aqua-tests/requirements.txt: -------------------------------------------------------------------------------- 1 | delegator.py==0.1.1 2 | pytest==7.3.0 3 | pytest-xdist==3.2.1 4 | pytest-repeat==0.9.1 5 | -------------------------------------------------------------------------------- /aqua-tests/src/aqua/main.aqua: -------------------------------------------------------------------------------- 1 | aqua Main 2 | 3 | import Dist from "@fluencelabs/aqua-lib/builtin.aqua" 4 | import "@fluencelabs/aqua-ipfs/ipfs-api.aqua" 5 | export get_external_api_multiaddr, load_blueprint_from_vault 6 | 7 | func load_blueprint_from_vault(node: string, cid: string) -> string: 8 | on node: 9 | dag <- dag_get(node, cid) 10 | blueprint <- Dist.load_blueprint(dag.path) 11 | <- blueprint 12 | 13 | -------------------------------------------------------------------------------- /aqua-tests/test_aqua.py: -------------------------------------------------------------------------------- 1 | import delegator 2 | import random 3 | import json 4 | import os 5 | import tempfile 6 | import inspect 7 | from config import get_local 8 | 9 | delegator.run("npx fluence dep npm i", block=True) 10 | default_peers = json.loads(delegator.run(f"node ./getDefaultPeers.js", block=True).out) 11 | 12 | 13 | def get_relays(): 14 | env = os.environ.get("FLUENCE_ENV") 15 | if env == "local": 16 | peers = get_local() 17 | else: 18 | if env is None: 19 | env = "testnet" 20 | peers = [peer["multiaddr"] for peer in default_peers[env]] 21 | 22 | assert len(peers) != 0, "No relays found" 23 | return peers 24 | 25 | 26 | relays = get_relays() 27 | peer_ids = [relay.split("/")[-1] for relay in relays] 28 | 29 | 30 | def get_random_list_item(ar): 31 | return ar[random.randint(0, len(ar) - 1)] 32 | 33 | 34 | def get_random_relay(): 35 | return get_random_list_item(relays) 36 | 37 | 38 | def get_random_peer_id(): 39 | return get_random_list_item(peer_ids) 40 | 41 | 42 | def run_aqua(func, args, relay=get_random_relay()): 43 | # "a" : arg1, "b" : arg2 ..... 44 | data = {chr(97 + i): arg for (i, arg) in enumerate(args)} 45 | call = f"{func}(" + ", ".join([chr(97 + i) for i in range(0, len(args))]) + ")" 46 | # inspect.stack method inspects the current execution stack as the name suggests 47 | # it's possible to infer that the minus 39th element of the stack always contains info 48 | # about the test function that is currently running. The third element is the function's name 49 | try: 50 | test_name = inspect.stack()[-39][3] 51 | except: 52 | # when running one test at a time, the stack is shorter so we need to use a different index 53 | test_name = inspect.stack()[-32][3] 54 | 55 | command = f"npx fluence run -k {test_name} --relay {relay} -f '{call}' --data '{json.dumps(data)}' --import 'node_modules' --quiet --particle-id" 56 | print(command) 57 | c = delegator.run(command, block=True) 58 | lines = c.out.splitlines() 59 | particle_id = lines[0] if len(lines) != 0 else "" 60 | 61 | if len(c.err.strip()) != 0: 62 | print(f"{particle_id}\n{c.err}") 63 | 64 | result = "\n".join(lines[1:]) 65 | 66 | try: 67 | result = json.loads(result) 68 | print(result) 69 | return result 70 | except: 71 | print(result) 72 | return result 73 | 74 | 75 | def put_dag(api, data): 76 | with tempfile.NamedTemporaryFile() as tmp: 77 | tmp.write(data.encode()) 78 | tmp.flush() 79 | c = delegator.run(f"ipfs --api {api} dag put {tmp.name}", block=True) 80 | if len(c.err.strip()) != 0: 81 | print(f"dag put error: {c.err}") 82 | return c.out.strip() 83 | 84 | 85 | def test_put_get_dag(): 86 | dag = """ 87 | { 88 | "name": "ipfs_pure", 89 | "dependencies": [ 90 | { 91 | "/": "bafkreibrmbfv7ab4dokljanddvq5nah66cdody2biusqgqlfqduwn4avdi" 92 | }, 93 | { 94 | "/": "bafybeicovoqrw75mskauoaknyxpla7xadtv5m2lphlrtjdj7dlacm6wawi" 95 | } 96 | ] 97 | } 98 | """ 99 | relay_multiaddr = get_random_relay() 100 | relay_peer_id = relay_multiaddr.split("/")[-1] 101 | ext_api_endpoint = run_aqua( 102 | "get_external_api_multiaddr", [relay_peer_id], relay=relay_multiaddr 103 | ) 104 | assert ext_api_endpoint["success"] == True 105 | cid = put_dag(ext_api_endpoint["multiaddr"], dag) 106 | assert cid != "" 107 | 108 | blueprint = run_aqua( 109 | "load_blueprint_from_vault", [relay_peer_id, cid], relay=relay_multiaddr 110 | ) 111 | 112 | assert blueprint["name"] == "ipfs_pure" 113 | assert ( 114 | blueprint["dependencies"][0]["/"] 115 | == "bafkreibrmbfv7ab4dokljanddvq5nah66cdody2biusqgqlfqduwn4avdi" 116 | ) 117 | assert ( 118 | blueprint["dependencies"][1]["/"] 119 | == "bafybeicovoqrw75mskauoaknyxpla7xadtv5m2lphlrtjdj7dlacm6wawi" 120 | ) 121 | -------------------------------------------------------------------------------- /aqua/README.md: -------------------------------------------------------------------------------- 1 | # Aqua API of AquaIPFS 2 | 3 | ## Documentation 4 | See [Aqua Book](https://fluence.dev/aqua-book/libraries/aqua-ipfs). 5 | 6 | ## API 7 | Public API is defined in [ipfs-api.aqua](/aqua/ipfs-api.aqua). 8 | 9 | aqua-ipfs service API is defined in [ipfs.aqua](/aqua/ipfs.aqua). 10 | 11 | ## How to use Aqua API in TypeScript 12 | There's a simple example in [example](/example/index.ts) 13 | 14 | ## How to update ipfs.aqua from ipfs_pure.wasm 15 | While in `aqua` directory, run `npm run generate-aqua` 16 | -------------------------------------------------------------------------------- /aqua/ipfs-api.aqua: -------------------------------------------------------------------------------- 1 | aqua IpfsApi declares * 2 | 3 | export get_and_cache 4 | export put, dag_put, dag_get, get_from, dag_get_from, cat_from 5 | export set_timeout, get_external_api_multiaddr 6 | export get_external_swarm_multiaddr, get_local_api_multiaddr 7 | 8 | import "@fluencelabs/aqua-lib/builtin.aqua" 9 | 10 | import "ipfs.aqua" 11 | 12 | alias Multiaddr: string 13 | 14 | -- Download file from remote IPFS node to Fluence node and then 15 | -- put that file to local IPFS node, effectively caching it on the local IPFS node. 16 | -- 17 | -- Arguments: 18 | -- node - PeerId of the node where execution should happen 19 | -- cid – IPFS Content ID to download 20 | -- from - Multiaddress of IPFS node to download `cid` from 21 | -- error - callback to notify function caller about errors 22 | -- 23 | -- Returns: 24 | -- Path on the node's local filesystem. It will be available only during single particle execution. 25 | -- 26 | -- Errors: 27 | -- If Ipfs.get_from or Ipfs.put fails, nil is returned. 28 | -- Errors are reported to the `error` callback. 29 | func get_and_cache( 30 | node: PeerId, 31 | cid: CID, 32 | from: Multiaddr, 33 | error: string, string -> () 34 | ) -> ?CID: 35 | -- localCid will be the same as cid 36 | localCid: *CID 37 | on node: 38 | -- Download file from remote IPFS to local filesystem 39 | get <- Ipfs.get_from(cid, from) 40 | if get.success: 41 | -- Put file to local IPFS node 42 | put <- Ipfs.put(get.path) 43 | if put.success: 44 | localCid <<- put.hash 45 | else: 46 | -- report error in background co-routine 47 | co error("Ipfs.put failed", put.error) 48 | else: 49 | -- report error in background co-routine 50 | co error("Ipfs.get failed", get.error) 51 | <- localCid 52 | 53 | -- Upload file `path` to IPFS node running on `node` 54 | -- path should exist & be available to `aqua-ipfs` 55 | func put(node: PeerId, path: string) -> IpfsPutResult: 56 | on node: 57 | result <- Ipfs.put(path) 58 | <- result 59 | 60 | -- Upload file `path` as a dag to IPFS node running on `node` 61 | -- path should exist & be available to `aqua-ipfs` 62 | func dag_put(node: PeerId, path: string) -> IpfsPutResult: 63 | on node: 64 | result <- Ipfs.dag_put(path) 65 | <- result 66 | 67 | -- Returns file path of the dag `cid` from local cache of IPFS node `node` 68 | func dag_get(node: PeerId, cid: CID) -> IpfsGetResult: 69 | on node: 70 | result <- Ipfs.dag_get(cid) 71 | <- result 72 | 73 | -- Download file `cid` from IPFS node `from` and save it to `node` 74 | func get_from(node: PeerId, cid: CID, from: Multiaddr) -> IpfsGetResult: 75 | on node: 76 | result <- Ipfs.get_from(cid, from) 77 | <- result 78 | 79 | -- Return contents of the dag `cid` from IPFS node `from` and save it to `node` 80 | func dag_get_from(node: PeerId, cid: CID, from: Multiaddr) -> IpfsGetResult: 81 | on node: 82 | result <- Ipfs.dag_get_from(cid, from) 83 | <- result 84 | 85 | -- Return contents of the file `cid` from IPFS node `from` 86 | func cat_from(node: PeerId, cid: CID, from: Multiaddr) -> IpfsCatResult: 87 | on node: 88 | result <- Ipfs.cat_from(cid, from) 89 | <- result 90 | 91 | -- Set timeout for IPFS calls in `aqua-ipfs` 92 | func set_timeout(node: PeerId, timeout_sec: u64): 93 | on node: 94 | Ipfs.set_timeout(timeout_sec) 95 | 96 | -- Get externally available multiaddress of IPFS's HTTP RPC endpoint (usually on port 5001) 97 | func get_external_api_multiaddr(node: PeerId) -> IpfsMultiaddrResult: 98 | on node: 99 | result <- Ipfs.get_external_api_multiaddr() 100 | <- result 101 | 102 | -- Get externally available multiaddress of IPFS's Swarm endpoint (usually on port 4001) 103 | func get_external_swarm_multiaddr(node: PeerId) -> IpfsMultiaddrResult: 104 | on node: 105 | result <- Ipfs.get_external_swarm_multiaddr() 106 | <- result 107 | 108 | -- Get local multiaddress of IPFS's HTTP RPC endpoint (usually on port 5001) 109 | func get_local_api_multiaddr(node: PeerId) -> IpfsMultiaddrResult: 110 | on node: 111 | result <- Ipfs.get_local_api_multiaddr() 112 | <- result 113 | -------------------------------------------------------------------------------- /aqua/ipfs.aqua: -------------------------------------------------------------------------------- 1 | aqua Ipfs declares * 2 | 3 | data IpfsCatResult: 4 | success: bool 5 | error: string 6 | contents: string 7 | 8 | data IpfsGetResult: 9 | success: bool 10 | error: string 11 | path: string 12 | 13 | data IpfsMultiaddrResult: 14 | success: bool 15 | error: string 16 | multiaddr: string 17 | 18 | data IpfsPutResult: 19 | success: bool 20 | error: string 21 | hash: string 22 | 23 | data IpfsResult: 24 | success: bool 25 | error: string 26 | 27 | service Ipfs("aqua-ipfs"): 28 | cat(hash: string) -> IpfsCatResult 29 | cat_from(hash: string, external_multiaddr: string) -> IpfsCatResult 30 | connect(multiaddr: string) -> IpfsResult 31 | dag_get(hash: string) -> IpfsGetResult 32 | dag_get_from(hash: string, external_multiaddr: string) -> IpfsGetResult 33 | dag_put(file_path: string) -> IpfsPutResult 34 | get(hash: string) -> IpfsGetResult 35 | get_external_api_multiaddr() -> IpfsMultiaddrResult 36 | get_external_swarm_multiaddr() -> IpfsMultiaddrResult 37 | get_from(hash: string, external_multiaddr: string) -> IpfsGetResult 38 | get_local_api_multiaddr() -> IpfsMultiaddrResult 39 | put(file_path: string) -> IpfsPutResult 40 | set_external_api_multiaddr(multiaddr: string) -> IpfsResult 41 | set_external_swarm_multiaddr(multiaddr: string) -> IpfsResult 42 | set_local_api_multiaddr(multiaddr: string) -> IpfsResult 43 | set_timeout(timeout_sec: u64) 44 | -------------------------------------------------------------------------------- /aqua/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fluencelabs/aqua-ipfs", 3 | "version": "0.6.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@fluencelabs/aqua-ipfs", 9 | "version": "0.6.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@fluencelabs/aqua-lib": "0.10.1" 13 | } 14 | }, 15 | "node_modules/@fluencelabs/aqua-lib": { 16 | "version": "0.10.1", 17 | "resolved": "https://registry.npmjs.org/@fluencelabs/aqua-lib/-/aqua-lib-0.10.1.tgz", 18 | "integrity": "sha512-zwg7faVsmUmfiV5EScVs0p+BArzJzFRfgjcigKzoxB5DvQRb64+OSs7Pmt8flSfV4iprPf1RQT9vhsDvdOsK0g==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /aqua/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fluencelabs/aqua-ipfs", 3 | "version": "0.6.0", 4 | "description": "Aqua IPFS library", 5 | "files": [ 6 | "*.aqua" 7 | ], 8 | "dependencies": { 9 | "@fluencelabs/aqua-lib": "0.10.1" 10 | }, 11 | "scripts": { 12 | "generate-aqua": "../service/build.sh", 13 | "compile-aqua": "fluence aqua -i . --dry", 14 | "build": "npm run compile-aqua" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/fluencelabs/aqua-ipfs", 19 | "directory": "aqua" 20 | }, 21 | "keywords": [ 22 | "aqua", 23 | "fluence", 24 | "ipfs", 25 | "p2p" 26 | ], 27 | "author": "Fluence Labs", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/fluencelabs/aqua-ipfs/issues" 31 | }, 32 | "homepage": "https://github.com/fluencelabs/aqua-ipfs#readme" 33 | } 34 | -------------------------------------------------------------------------------- /builtin-package/ipfs_effector_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipfs_effector", 3 | "mounted_binaries": 4 | { 5 | "ipfs": "/usr/bin/ipfs" 6 | }, 7 | "mem_page_count": 1, 8 | "preopened_files": [ 9 | "/tmp" 10 | ], 11 | "mapped_dirs": { 12 | "tmp": "./tmp" 13 | }, 14 | "logger_enabled": true 15 | } 16 | -------------------------------------------------------------------------------- /builtin-package/ipfs_pure_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipfs_pure", 3 | "mem_page_count": 1, 4 | "preopened_files": [ 5 | "/tmp" 6 | ], 7 | "mapped_dirs": { 8 | "tmp": "./tmp" 9 | }, 10 | "logger_enabled": true 11 | } 12 | -------------------------------------------------------------------------------- /builtin-package/on_start.air: -------------------------------------------------------------------------------- 1 | (seq 2 | (seq 3 | (call relay ("aqua-ipfs" "set_local_api_multiaddr") [local_api_multiaddr] local_api_res) 4 | (xor 5 | (match local_api_res.$.success! true 6 | (null) 7 | ) 8 | (call relay ("op" "return") [local_api_res.$.error!]) 9 | ) 10 | ) 11 | (seq 12 | (call relay ("aqua-ipfs" "set_external_api_multiaddr") [external_api_multiaddr] external_api_res) 13 | (xor 14 | (match external_api_res.$.success! true 15 | (call relay ("op" "return") [true]) 16 | ) 17 | (call relay ("op" "return") [external_api_res.$.error!]) 18 | ) 19 | ) 20 | ) 21 | -------------------------------------------------------------------------------- /builtin-package/on_start.json: -------------------------------------------------------------------------------- 1 | { 2 | "local_api_multiaddr": "$FLUENCE_ENV_AQUA_IPFS_LOCAL_API_MULTIADDR", 3 | "external_api_multiaddr": "$FLUENCE_ENV_AQUA_IPFS_EXTERNAL_API_MULTIADDR" 4 | } 5 | -------------------------------------------------------------------------------- /builtin-package/package.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o pipefail -o nounset -o errexit 3 | 4 | # set current working directory to script directory to run script from everywhere 5 | cd "$(dirname "$0")" 6 | PACKAGE_DIR="$(pwd)/../aqua-ipfs" 7 | 8 | ( 9 | rm -rf $PACKAGE_DIR/* 10 | mkdir -p $PACKAGE_DIR 11 | ) 12 | 13 | ( 14 | echo "*** copy wasm files ***" 15 | cd ../service 16 | cp artifacts/*.wasm "$PACKAGE_DIR" 17 | ) 18 | 19 | ( 20 | echo "*** copy on_start script ***" 21 | cp on_start.json "$PACKAGE_DIR" 22 | cp on_start.air "$PACKAGE_DIR" 23 | ) 24 | 25 | PURE_CID=$(ipfs add -q --only-hash --cid-version=1 --chunker=size-262144 $PACKAGE_DIR/ipfs_pure.wasm) 26 | EFFECTOR_CID=$(ipfs add -q --only-hash --cid-version=1 --chunker=size-262144 $PACKAGE_DIR/ipfs_effector.wasm) 27 | mv $PACKAGE_DIR/ipfs_pure.wasm "$PACKAGE_DIR"/"$PURE_CID".wasm 28 | mv $PACKAGE_DIR/ipfs_effector.wasm "$PACKAGE_DIR"/"$EFFECTOR_CID".wasm 29 | cp ipfs_pure_config.json "$PACKAGE_DIR"/"$PURE_CID"_config.json 30 | cp ipfs_effector_config.json "$PACKAGE_DIR"/"$EFFECTOR_CID"_config.json 31 | 32 | # write blueprint.json 33 | echo "{}" | jq --arg pure_cid "$PURE_CID" --arg effector_cid "$EFFECTOR_CID" '{"name": "ipfs_pure", "dependencies":[{"/":$effector_cid},{"/":$pure_cid}]}' > "$PACKAGE_DIR/blueprint.json" 34 | 35 | ( 36 | echo "*** create builtin distribution package ***" 37 | cd .. 38 | tar -f aqua-ipfs.tar.gz -zcvv ./aqua-ipfs 39 | ) 40 | 41 | echo "*** done ***" 42 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Run example on TestNet 2 | While in `example` directory, `npm run start` will run `index.ts` against Fluence TestNet 3 | 4 | # Run example locally 5 | To run example locally: 6 | 1. Spin up local environment through [Fluence CLI](https://github.com/fluencelabs/cli)'s `local` command 7 | 2. Pass local fluence peers multi addresses to `main` in `index.ts` 8 | 3. (Optional) Change `IPFS_MULTIADDR` to address of your preferred IPFS node 9 | -------------------------------------------------------------------------------- /example/aqua/export.aqua: -------------------------------------------------------------------------------- 1 | aqua Exports 2 | 3 | import put, get_from, set_timeout from "@fluencelabs/aqua-ipfs/ipfs-api.aqua" 4 | export put, get_from, set_timeout 5 | -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Fluence, testNet, Relay } from "@fluencelabs/js-client"; 17 | 18 | import { put, get_from, set_timeout } from "./generated/export.js"; 19 | 20 | import { multiaddr } from "@multiformats/multiaddr"; 21 | import { create } from "kubo-rpc-client"; 22 | import all from "it-all"; 23 | import uint8ArrayConcat from "uint8arrays/concat.js"; 24 | 25 | // Multi address of the IPFS node 26 | // we will work with through the Fluence Network 27 | const IPFS_MULTIADDR = multiaddr("/dns4/ipfs.fluence.dev/tcp/5001"); 28 | 29 | /** 30 | * @param environment - array of fluence network nodes (two are needed) 31 | * @note Pass addresses of local nodes to experiment locally 32 | */ 33 | async function main(environment: Relay[]) { 34 | const relay = environment[0]; 35 | const node = environment[1]; 36 | 37 | const ipfs = await create({ url: IPFS_MULTIADDR }); 38 | console.log("📗 Created IPFS HTTP Client"); 39 | 40 | const content = "Hola, Fluence!"; 41 | const encoder = new TextEncoder(); 42 | 43 | const added = await ipfs.add(encoder.encode(content)); 44 | console.log("📗 Uploaded content, got CID:", added.cid.toString()); 45 | 46 | let stream = await ipfs.cat(added.path); 47 | let data = uint8ArrayConcat(await all(stream)); 48 | const decoder = new TextDecoder(); 49 | console.log("📗 Retrieved content: ", decoder.decode(data)); 50 | 51 | await Fluence.connect(relay); 52 | const client = Fluence.getClient(); 53 | 54 | console.log( 55 | "📗 Created a Fluence Peer %s with Relay %s", 56 | client.getPeerId(), 57 | client.getRelayPeerId() 58 | ); 59 | 60 | // default IPFS timeout is 1 sec, 61 | // set to 10 secs to retrieve file from remote node 62 | await set_timeout(node.peerId, 10); 63 | console.log("📘 Ipfs.set_timeout"); 64 | 65 | let getResult = await get_from( 66 | node.peerId, 67 | added.cid.toString(), 68 | IPFS_MULTIADDR.toString(), 69 | { ttl: 20000 } 70 | ); 71 | console.log("📘 Ipfs.get_from", getResult); 72 | 73 | let putResult = await put(node.peerId, getResult.path, { 74 | ttl: 20000, 75 | }); 76 | console.log("📘 Ipfs.put", putResult); 77 | 78 | await ipfs.stop(); 79 | } 80 | 81 | main(testNet) 82 | .then(() => process.exit(0)) 83 | .catch((error) => { 84 | console.error(error); 85 | process.exit(1); 86 | }); 87 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "ipfs-aqua-example", 4 | "version": "1.0.0", 5 | "description": "A simple example of how to use ipfs-aqua in TS", 6 | "main": "index.js", 7 | "scripts": { 8 | "compile-aqua": "fluence aqua -i aqua -o generated", 9 | "prebuild": "npm run compile-aqua", 10 | "build": "tsc", 11 | "start": "node dist/index.js", 12 | "prestart": "npm run build" 13 | }, 14 | "author": "Fluence Labs", 15 | "license": "MIT", 16 | "dependencies": { 17 | "@fluencelabs/aqua-ipfs": "^0.5.29", 18 | "@fluencelabs/aqua-lib": "^0.9.0", 19 | "@fluencelabs/js-client": "^0.4.2", 20 | "kubo-rpc-client": "^3.0.2", 21 | "@multiformats/multiaddr": "^12.1.1", 22 | "it-all": "^1.0.5", 23 | "uint8arrays": "^2.1.5" 24 | }, 25 | "devDependencies": { 26 | "typescript": "^4.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | 43 | /* Module Resolution Options */ 44 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 48 | // "typeRoots": [], /* List of folders to include type definitions from. */ 49 | // "types": [], /* Type declaration files to be included in compilation. */ 50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 51 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 53 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 54 | 55 | /* Source Map Options */ 56 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 60 | 61 | /* Experimental Options */ 62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 64 | 65 | /* Advanced Options */ 66 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /local-network/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # management secret key is NAB5rGwT4qOEB+6nLQawkTfCOV2eiFSjgQK8bfEdZXY= 2 | services: 3 | fluence-0: # /ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK 4 | command: -f ed25519 -k 29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7770 -w 9990 --bootstraps /dns4/fluence-1/tcp/7771 /dns4/fluence-2/tcp/7772 5 | container_name: fluence-0 6 | environment: 7 | RUST_BACKTRACE: full 8 | RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info 9 | WASM_LOG: info 10 | image: fluencelabs/node:latest 11 | ports: 12 | - 7770:7770 # tcp 13 | - 9990:9990 # ws 14 | - 5000:5001 # ipfs rpc 15 | - 4000:4001 # ipfs swarm 16 | - 18080:18080 # /metrics 17 | restart: always 18 | volumes: 19 | - fluence-0:/.fluence 20 | - data-0:/config 21 | networks: 22 | - fluence 23 | 24 | fluence-1: # /ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv 25 | command: -f ed25519 -k 5fNENMwkUT4dW3hPs9ZwqV4qA5pdTtUChTazAx9Awe2Vpz1yaJu3VCmcEZow6YgdFBGoZoFAZUZBbF3c2Ebd2iL -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7771 -w 9991 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-2/tcp/7772 #/dns4/kras-00.fluence.dev/tcp/7770 26 | container_name: fluence-1 27 | environment: 28 | RUST_BACKTRACE: full 29 | RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info 30 | WASM_LOG: info 31 | image: fluencelabs/node:latest 32 | ports: 33 | - 7771:7771 # tcp 34 | - 9991:9991 # ws 35 | - 5001:5001 # ipfs rpc 36 | - 4001:4001 # ipfs swarm 37 | - 18081:18080 # /metrics 38 | restart: always 39 | volumes: 40 | - fluence-1:/.fluence 41 | - data-1:/config 42 | networks: 43 | - fluence 44 | 45 | fluence-2: # /ip4/127.0.0.1/tcp/9992/ws/p2p/12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5 46 | command: -f ed25519 -k 5DTs9LQS8Ay2dM8xBcikDRwYLMcanhsC6tynSSgpLyBZEv5Ey34LVw1fYcCuUj9A9EfvQJB2bsaGhSRoHQ7D6UE5 -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7772 -w 9992 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-1/tcp/7771 #/dns4/kras-00.fluence.dev/tcp/7770 47 | container_name: fluence-2 48 | environment: 49 | RUST_BACKTRACE: full 50 | RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info 51 | WASM_LOG: info 52 | image: fluencelabs/node:latest 53 | ports: 54 | - 7772:7772 # tcp 55 | - 9992:9992 # ws 56 | - 5002:5001 # ipfs rpc 57 | - 4002:4001 # ipfs swarm 58 | - 18082:18080 # /metrics 59 | restart: always 60 | volumes: 61 | - fluence-2:/.fluence 62 | - data-2:/config 63 | networks: 64 | - fluence 65 | 66 | version: "3.5" 67 | volumes: 68 | fluence-0: 69 | fluence-1: 70 | fluence-2: 71 | data-0: 72 | data-1: 73 | data-2: 74 | 75 | networks: 76 | fluence: 77 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2023-12-06" 3 | targets = [ "x86_64-unknown-linux-gnu", "wasm32-wasi", "x86_64-apple-darwin" ] 4 | components = [ "rustfmt", "clippy" ] 5 | -------------------------------------------------------------------------------- /service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "effector", 4 | "pure", 5 | "distro", 6 | "types", 7 | ] 8 | [workspace.dependencies] 9 | marine-rs-sdk = { version = "0.13.0", features = ["logger"] } 10 | marine-rs-sdk-test = "0.14.0" 11 | -------------------------------------------------------------------------------- /service/artifacts/Config.toml: -------------------------------------------------------------------------------- 1 | total_memory_limit = "Infinity" 2 | 3 | [[module]] 4 | name = "ipfs_effector" 5 | mem_pages_count = 100 6 | logger_enabled = true 7 | 8 | [module.mounted_binaries] 9 | ipfs = "/usr/bin/ipfs" 10 | 11 | [module.wasi] 12 | preopend_files = ["tmp"] 13 | mapped_dirs = { "tmp" = "." } 14 | 15 | [[module]] 16 | name = "ipfs_pure" 17 | logger_enabled = true 18 | -------------------------------------------------------------------------------- /service/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit -o nounset -o pipefail 3 | 4 | # set current working directory to script directory to run script from everywhere 5 | cd "$(dirname "$0")" 6 | 7 | # This script builds all subprojects and puts all created Wasm modules in one dir 8 | cd effector 9 | marine build --release 10 | 11 | cd ../pure 12 | marine build --release 13 | 14 | cd .. 15 | mkdir -p artifacts 16 | rm -f artifacts/*.wasm 17 | cp target/wasm32-wasi/release/ipfs_effector.wasm artifacts/ 18 | cp target/wasm32-wasi/release/ipfs_pure.wasm artifacts/ 19 | marine aqua artifacts/ipfs_pure.wasm -s Ipfs -i aqua-ipfs >../aqua/ipfs.aqua 20 | 21 | mkdir -p distro/ipfs-service 22 | cp -v artifacts/ipfs_effector.wasm artifacts/ipfs_pure.wasm artifacts/Config.toml distro/ipfs-service 23 | cd distro 24 | cargo build 25 | -------------------------------------------------------------------------------- /service/distro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aqua-ipfs-distro" 3 | version = "0.6.0" 4 | description = "distribution of aqua ipfs service" 5 | edition = "2021" 6 | build = "build.rs" 7 | license = "Apache-2.0" 8 | include = [ "/src", "/ipfs-service", "build.rs", "Cargo.toml" ] 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | maplit = "1.0.2" 14 | 15 | [build-dependencies] 16 | built = "0.7.0" 17 | -------------------------------------------------------------------------------- /service/distro/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | built::write_built_file().expect("Failed to acquire build-time information") 3 | } 4 | -------------------------------------------------------------------------------- /service/distro/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub const IPFS_EFFECTOR: &[u8] = include_bytes!("../ipfs-service/ipfs_effector.wasm"); 2 | pub const IPFS_FACADE: &[u8] = include_bytes!("../ipfs-service/ipfs_pure.wasm"); 3 | pub const CONFIG: &[u8] = include_bytes!("../ipfs-service/Config.toml"); 4 | 5 | pub mod build_info { 6 | include!(concat!(env!("OUT_DIR"), "/built.rs")); 7 | } 8 | 9 | pub use build_info::PKG_VERSION as VERSION; 10 | 11 | pub fn modules() -> std::collections::HashMap<&'static str, &'static [u8]> { 12 | maplit::hashmap! { 13 | "ipfs_pure" => IPFS_FACADE, 14 | "ipfs_effector" => IPFS_EFFECTOR, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/effector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipfs-effector" 3 | version = "0.6.0" 4 | authors = ["Fluence Labs"] 5 | edition = "2018" 6 | publish = false 7 | 8 | [[bin]] 9 | name = "ipfs_effector" 10 | path = "src/main.rs" 11 | 12 | [dependencies] 13 | marine-rs-sdk = { workspace = true } 14 | multiaddr = { version = "0.11.2", package = "fluence-fork-parity-multiaddr" } 15 | log = "0.4.20" 16 | eyre = "0.6.8" 17 | serde_json = "1.0.64" 18 | itertools = "0.12.0" 19 | 20 | types = { path = "../types" } 21 | 22 | [dev-dependencies] 23 | marine-rs-sdk-test = { workspace = true } 24 | 25 | [build-dependencies] 26 | marine-rs-sdk-test = { workspace = true } 27 | -------------------------------------------------------------------------------- /service/effector/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | use marine_rs_sdk_test::generate_marine_test_env; 17 | use marine_rs_sdk_test::ServiceDescription; 18 | 19 | fn main() { 20 | let services = vec![ 21 | ( 22 | "ipfs_put".to_string(), 23 | ServiceDescription { 24 | config_path: "tests/Config_put.toml".to_string(), 25 | modules_dir: None, 26 | }, 27 | ), 28 | ( 29 | "ipfs_error".to_string(), 30 | ServiceDescription { 31 | config_path: "tests/Config_error.toml".to_string(), 32 | modules_dir: None, 33 | }, 34 | ), 35 | ]; 36 | 37 | let target = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 38 | if target != "wasm32" { 39 | generate_marine_test_env(services, "marine_test_env.rs", file!()); 40 | } 41 | 42 | println!("cargo:rerun-if-changed=tests.rs"); 43 | } 44 | -------------------------------------------------------------------------------- /service/effector/src/effector.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #![allow(improper_ctypes)] 18 | 19 | use eyre::{Result, WrapErr}; 20 | use marine_rs_sdk::marine; 21 | use marine_rs_sdk::module_manifest; 22 | use marine_rs_sdk::MountedBinaryResult; 23 | use marine_rs_sdk::WasmLoggerBuilder; 24 | use std::fs; 25 | 26 | use itertools::Itertools; 27 | use types::{IpfsCatResult, IpfsGetPeerIdResult, IpfsPutResult, IpfsResult}; 28 | 29 | module_manifest!(); 30 | 31 | /// Default chunk size for `ipfs add` command to produce stable CIDs. 32 | const CHUCK_SIZE: usize = 262144; 33 | 34 | pub fn main() { 35 | WasmLoggerBuilder::new() 36 | .with_log_level(log::LevelFilter::Info) 37 | .build() 38 | .unwrap(); 39 | } 40 | 41 | /// Run `ipfs` mounted binary with the specified arguments 42 | fn run_ipfs(cmd: Vec) -> Result { 43 | let result = ipfs(cmd.clone()); 44 | 45 | result 46 | .into_std() 47 | .ok_or(eyre::eyre!( 48 | "stdout or stderr contains non valid UTF8 string" 49 | ))? 50 | .map_err(|e| eyre::eyre!("ipfs cli call failed \n{:?}: {}", cmd.iter().join(" "), e)) 51 | } 52 | 53 | #[inline] 54 | fn format_timeout(timeout_sec: u64) -> String { 55 | format!("{}s", timeout_sec) 56 | } 57 | 58 | fn make_cmd_args(args: Vec, api_multiaddr: String, timeout_sec: u64) -> Vec { 59 | args.into_iter() 60 | .chain(vec![ 61 | String::from("--timeout"), 62 | format_timeout(timeout_sec), 63 | String::from("--api"), 64 | api_multiaddr, 65 | ]) 66 | .collect() 67 | } 68 | 69 | #[marine] 70 | pub fn connect(multiaddr: String, api_multiaddr: String, timeout_sec: u64) -> IpfsResult { 71 | let args = vec![String::from("swarm"), String::from("connect"), multiaddr]; 72 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 73 | 74 | run_ipfs(cmd).map(|_| ()).into() 75 | } 76 | 77 | /// Put file from specified path to IPFS and return its hash. 78 | #[marine] 79 | pub fn put(file_path: String, api_multiaddr: String, timeout_sec: u64) -> IpfsPutResult { 80 | if !std::path::Path::new(&file_path).exists() { 81 | return IpfsPutResult { 82 | success: false, 83 | error: format!("path {} doesn't exist", file_path), 84 | hash: "".to_string(), 85 | }; 86 | } 87 | 88 | let args = vec![ 89 | String::from("add"), 90 | String::from("-Q"), 91 | inject_vault_host_path(file_path), 92 | String::from("--cid-version=1"), 93 | format!("--chunker=size-{}", CHUCK_SIZE), 94 | ]; 95 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 96 | run_ipfs(cmd).map(|res| res.trim().to_string()).into() 97 | } 98 | 99 | /// Put dag from specified path to IPFS and return its hash. 100 | #[marine] 101 | pub fn dag_put(file_path: String, api_multiaddr: String, timeout_sec: u64) -> IpfsPutResult { 102 | if !std::path::Path::new(&file_path).exists() { 103 | return IpfsPutResult { 104 | success: false, 105 | error: format!("path {} doesn't exist", file_path), 106 | hash: "".to_string(), 107 | }; 108 | } 109 | 110 | let args = vec![ 111 | String::from("dag"), 112 | String::from("put"), 113 | inject_vault_host_path(file_path), 114 | ]; 115 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 116 | run_ipfs(cmd).map(|res| res.trim().to_string()).into() 117 | } 118 | 119 | /// Get file by provided hash from IPFS, save it to a `file_path`, and return that path 120 | #[marine] 121 | pub fn get(hash: String, file_path: &str, api_multiaddr: String, timeout_sec: u64) -> IpfsResult { 122 | let args = vec![ 123 | String::from("get"), 124 | String::from("-o"), 125 | inject_vault_host_path(file_path.to_string()), 126 | hash, 127 | ]; 128 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 129 | 130 | run_ipfs(cmd).map(drop).into() 131 | } 132 | 133 | /// Get dag by provided hash from IPFS, save it to a `file_path`, and return that path 134 | #[marine] 135 | pub fn dag_get( 136 | hash: String, 137 | file_path: &str, 138 | api_multiaddr: String, 139 | timeout_sec: u64, 140 | ) -> IpfsResult { 141 | let args = vec![String::from("dag"), String::from("get"), hash]; 142 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 143 | 144 | let result: Result<()> = try { 145 | let dag = run_ipfs(cmd)?; 146 | fs::write(inject_vault_host_path(file_path.to_string()), dag)? 147 | }; 148 | 149 | result 150 | .map(|_| ()) 151 | .map_err(|e| eyre::eyre!("dag_get error: {:?}", e)) 152 | .into() 153 | } 154 | 155 | #[marine] 156 | pub fn get_peer_id(api_multiaddr: String, timeout_sec: u64) -> IpfsGetPeerIdResult { 157 | let result: Result = try { 158 | let cmd = make_cmd_args(vec![String::from("id")], api_multiaddr, timeout_sec); 159 | 160 | let result = run_ipfs(cmd)?; 161 | let result: serde_json::Value = 162 | serde_json::from_str(&result).wrap_err("ipfs response parsing failed")?; 163 | result 164 | .get("ID") 165 | .ok_or(eyre::eyre!("ID field not found in response"))? 166 | .as_str() 167 | .ok_or(eyre::eyre!("ID value is not string"))? 168 | .to_string() 169 | }; 170 | 171 | result 172 | .map_err(|e| eyre::eyre!("get_peer_id error: {:?}", e)) 173 | .into() 174 | } 175 | 176 | /// Cat file by provided hash from IPFS, 177 | #[marine] 178 | pub fn cat(hash: String, api_multiaddr: String, timeout_sec: u64) -> IpfsCatResult { 179 | let args = vec![String::from("cat"), hash]; 180 | let cmd = make_cmd_args(args, api_multiaddr, timeout_sec); 181 | 182 | run_ipfs(cmd) 183 | .map_err(|e| eyre::eyre!("ipfs cat error: {:?}", e)) 184 | .into() 185 | } 186 | 187 | #[marine] 188 | #[host_import] 189 | extern "C" { 190 | /// Execute provided cmd as a parameters of ipfs cli, return result. 191 | pub fn ipfs(cmd: Vec) -> MountedBinaryResult; 192 | } 193 | 194 | fn inject_vault_host_path(path: String) -> String { 195 | let vault = "/tmp/vault"; 196 | if let Some(stripped) = path.strip_prefix(&vault) { 197 | let host_vault_path = std::env::var(vault).expect("vault must be mapped to /tmp/vault"); 198 | format!("/{}/{}", host_vault_path, stripped) 199 | } else { 200 | path 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /service/effector/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #![feature(try_blocks)] 18 | 19 | #[cfg(target_arch = "wasm32")] 20 | mod effector; 21 | 22 | #[cfg(target_arch = "wasm32")] 23 | pub fn main() { 24 | effector::main() 25 | } 26 | 27 | #[cfg(not(target_arch = "wasm32"))] 28 | pub fn main() {} 29 | -------------------------------------------------------------------------------- /service/effector/tests/Config.toml: -------------------------------------------------------------------------------- 1 | modules_dir = "../../artifacts/" 2 | total_memory_limit = "Infinity" 3 | 4 | [[module]] 5 | name = "ipfs_effector" 6 | mem_pages_count = 100 7 | logger_enabled = true 8 | 9 | [module.mounted_binaries] 10 | ipfs = "ipfs" 11 | -------------------------------------------------------------------------------- /service/effector/tests/Config_error.toml: -------------------------------------------------------------------------------- 1 | modules_dir = "../../artifacts/" 2 | total_memory_limit = "Infinity" 3 | 4 | [[module]] 5 | name = "ipfs_effector" 6 | mem_pages_count = 100 7 | logger_enabled = true 8 | 9 | [module.mounted_binaries] 10 | ipfs = "ipfs_error" 11 | -------------------------------------------------------------------------------- /service/effector/tests/Config_put.toml: -------------------------------------------------------------------------------- 1 | modules_dir = "../../artifacts/" 2 | total_memory_limit = "Infinity" 3 | 4 | [[module]] 5 | name = "ipfs_effector" 6 | mem_pages_count = 100 7 | logger_enabled = true 8 | 9 | [module.mounted_binaries] 10 | ipfs = "ipfs_put" 11 | -------------------------------------------------------------------------------- /service/effector/tests/ipfs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | peer_id='{"ID": "12D3KooWF2hVw8p5W7iv1NWeKcjkqtJhYKSrWN6ZYq15idxkjEpm"}' 3 | echo $peer_id 4 | exit 0 5 | -------------------------------------------------------------------------------- /service/effector/tests/ipfs_error: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exit 1 3 | -------------------------------------------------------------------------------- /service/effector/tests/ipfs_put: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | printf "hash\n" 3 | exit 0 4 | -------------------------------------------------------------------------------- /service/effector/tests/tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | marine_rs_sdk_test::include_test_env!("/marine_test_env.rs"); 20 | 21 | #[test] 22 | fn connect_failed() { 23 | let mut effector = marine_test_env::ipfs_error::ServiceInterface::new(); 24 | let result = effector.connect( 25 | "/ip4/127.0.0.1/tcp/5001".to_string(), 26 | "/ip4/127.0.0.1/tcp/5001".to_string(), 27 | 5u64, 28 | ); 29 | assert!(!result.success); 30 | } 31 | 32 | #[test] 33 | fn put_result() { 34 | let mut effector = marine_test_env::ipfs_put::ServiceInterface::new(); 35 | let result = effector.put("tmp".to_string(), "api_multiaddr".to_string(), 1); 36 | assert_eq!("hash", result.hash); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /service/pure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipfs-pure" 3 | version = "0.6.0" 4 | authors = ["Fluence Labs"] 5 | edition = "2018" 6 | publish = false 7 | 8 | [[bin]] 9 | name = "ipfs_pure" 10 | path = "src/main.rs" 11 | 12 | [dependencies] 13 | marine-rs-sdk = { workspace = true } 14 | multiaddr = { version = "0.11.2", package = "fluence-fork-parity-multiaddr" } 15 | log = "0.4.20" 16 | eyre = "0.6.8" 17 | toml = "0.8.8" 18 | serde = "1.0.188" 19 | bs58 = "0.5.0" 20 | 21 | types = { path = "../types" } 22 | 23 | [dev-dependencies] 24 | marine-rs-sdk-test = { workspace = true } 25 | 26 | [build-dependencies] 27 | marine-rs-sdk-test = { workspace = true } 28 | -------------------------------------------------------------------------------- /service/pure/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | use marine_rs_sdk_test::generate_marine_test_env; 17 | use marine_rs_sdk_test::ServiceDescription; 18 | 19 | fn main() { 20 | let services = vec![( 21 | "ipfs".to_string(), 22 | ServiceDescription { 23 | config_path: "tests/Config.toml".to_string(), 24 | modules_dir: Some("../artifacts".to_string()), 25 | }, 26 | )]; 27 | 28 | let target = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 29 | if target != "wasm32" { 30 | generate_marine_test_env(services, "marine_test_env.rs", file!()); 31 | } 32 | 33 | println!("cargo:rerun-if-changed=tests.rs"); 34 | } 35 | -------------------------------------------------------------------------------- /service/pure/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #![feature(try_blocks)] 18 | 19 | #[cfg(target_arch = "wasm32")] 20 | mod pure; 21 | 22 | #[cfg(target_arch = "wasm32")] 23 | pub fn main() { 24 | pure::main() 25 | } 26 | 27 | #[cfg(not(target_arch = "wasm32"))] 28 | pub fn main() {} 29 | -------------------------------------------------------------------------------- /service/pure/src/pure.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #![allow(improper_ctypes)] 18 | 19 | use types::{ 20 | IpfsCatResult, IpfsGetPeerIdResult, IpfsGetResult, IpfsMultiaddrResult, IpfsPutResult, 21 | IpfsResult, 22 | }; 23 | 24 | use marine_rs_sdk::marine; 25 | use marine_rs_sdk::module_manifest; 26 | use marine_rs_sdk::ParticleParameters; 27 | use marine_rs_sdk::WasmLoggerBuilder; 28 | 29 | use eyre::WrapErr; 30 | use multiaddr::{multihash::Multihash, Multiaddr, Protocol}; 31 | use serde::{Deserialize, Serialize}; 32 | use std::fs; 33 | use std::str::FromStr; 34 | 35 | const CONFIG_FILE_PATH: &str = "/tmp/multiaddr_config"; 36 | const DEFAULT_TIMEOUT_SEC: u64 = 10u64; 37 | module_manifest!(); 38 | 39 | pub fn main() { 40 | WasmLoggerBuilder::new() 41 | .with_log_level(log::LevelFilter::Info) 42 | .build() 43 | .unwrap(); 44 | reset_config(); 45 | } 46 | 47 | #[derive(Deserialize, Serialize)] 48 | pub struct Config { 49 | pub timeout: u64, 50 | pub external_api_multiaddr: Option, 51 | pub external_swarm_multiaddr: Option, 52 | pub local_api_multiaddr: Option, 53 | } 54 | 55 | fn save_external_api_multiaddr(multiaddr: Multiaddr) { 56 | let mut config = load_config(); 57 | config.external_api_multiaddr = Some(multiaddr); 58 | write_config(config); 59 | } 60 | 61 | fn load_external_api_multiaddr() -> eyre::Result { 62 | load_config() 63 | .external_api_multiaddr 64 | .ok_or(eyre::eyre!("external api multiaddr is not set")) 65 | } 66 | 67 | fn save_local_api_multiaddr(multiaddr: Multiaddr) { 68 | let mut config = load_config(); 69 | config.local_api_multiaddr = Some(multiaddr); 70 | write_config(config); 71 | } 72 | 73 | fn load_local_api_multiaddr() -> eyre::Result { 74 | load_config() 75 | .local_api_multiaddr 76 | .ok_or(eyre::eyre!("local api multiaddr is not set")) 77 | } 78 | 79 | pub fn write_config(config: Config) { 80 | fs::write(CONFIG_FILE_PATH, toml::to_string(&config).unwrap()).unwrap(); 81 | } 82 | 83 | pub fn load_config() -> Config { 84 | let file_content = fs::read_to_string(CONFIG_FILE_PATH).unwrap(); 85 | let config: Config = toml::from_str(&file_content).unwrap(); 86 | config 87 | } 88 | 89 | pub(crate) fn reset_config() { 90 | write_config(Config { 91 | timeout: DEFAULT_TIMEOUT_SEC, 92 | external_api_multiaddr: None, 93 | external_swarm_multiaddr: None, 94 | local_api_multiaddr: None, 95 | }); 96 | } 97 | 98 | pub fn get_peer_id(api_multiaddr: String, timeout: u64) -> eyre::Result> { 99 | let peer_id_result = ipfs_get_peer_id(api_multiaddr, timeout); 100 | if !peer_id_result.success { 101 | Err(eyre::eyre!(peer_id_result.error.clone()))?; 102 | } 103 | 104 | Ok(Protocol::P2p( 105 | Multihash::from_bytes(&bs58::decode(peer_id_result.peer_id.clone()).into_vec()?).wrap_err( 106 | format!( 107 | "error parsing peer_id from 'ipfs id': {}", 108 | peer_id_result.peer_id 109 | ), 110 | )?, 111 | )) 112 | } 113 | 114 | fn vault_path(particle: &ParticleParameters) -> String { 115 | format!("/tmp/vault/{}-{}", particle.id, particle.token) 116 | } 117 | 118 | #[marine] 119 | pub fn connect(multiaddr: String) -> IpfsResult { 120 | if Multiaddr::from_str(&multiaddr).is_err() { 121 | return Err(eyre::eyre!("invalid multiaddr: {}", multiaddr)).into(); 122 | } 123 | 124 | let timeout = load_config().timeout; 125 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 126 | 127 | if local_maddr.is_ok() { 128 | ipfs_connect(multiaddr, local_maddr.unwrap(), timeout) 129 | } else { 130 | local_maddr.map(drop).into() 131 | } 132 | } 133 | 134 | #[marine] 135 | pub fn put(file_path: String) -> IpfsPutResult { 136 | let timeout = load_config().timeout; 137 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 138 | if local_maddr.is_ok() { 139 | ipfs_put(file_path, local_maddr.unwrap(), timeout) 140 | } else { 141 | local_maddr.into() 142 | } 143 | } 144 | 145 | #[marine] 146 | pub fn dag_put(file_path: String) -> IpfsPutResult { 147 | let timeout = load_config().timeout; 148 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 149 | if local_maddr.is_ok() { 150 | ipfs_dag_put(file_path, local_maddr.unwrap(), timeout) 151 | } else { 152 | local_maddr.into() 153 | } 154 | } 155 | 156 | #[marine] 157 | pub fn get(hash: String) -> IpfsGetResult { 158 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 159 | if local_maddr.is_ok() { 160 | get_from(hash, local_maddr.unwrap()) 161 | } else { 162 | local_maddr.into() 163 | } 164 | } 165 | 166 | #[marine] 167 | pub fn get_from(hash: String, external_multiaddr: String) -> IpfsGetResult { 168 | let config = load_config(); 169 | let timeout = config.timeout; 170 | 171 | if Multiaddr::from_str(&external_multiaddr).is_err() { 172 | return Err(eyre::eyre!("invalid multiaddr: {}", external_multiaddr)).into(); 173 | } 174 | 175 | let particle = marine_rs_sdk::get_call_parameters().particle; 176 | let particle_vault_path = vault_path(&particle); 177 | let path = format!("{}/{}", particle_vault_path, hash); 178 | let get_result = ipfs_get(hash, &path, external_multiaddr, timeout); 179 | 180 | if get_result.success { 181 | Ok(path).into() 182 | } else { 183 | Err(eyre::eyre!(get_result.error)).into() 184 | } 185 | } 186 | 187 | #[marine] 188 | pub fn dag_get(hash: String) -> IpfsGetResult { 189 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 190 | if local_maddr.is_ok() { 191 | dag_get_from(hash, local_maddr.unwrap()) 192 | } else { 193 | local_maddr.into() 194 | } 195 | } 196 | 197 | #[marine] 198 | pub fn dag_get_from(hash: String, external_multiaddr: String) -> IpfsGetResult { 199 | let config = load_config(); 200 | let timeout = config.timeout; 201 | 202 | if Multiaddr::from_str(&external_multiaddr).is_err() { 203 | return Err(eyre::eyre!("invalid multiaddr: {}", external_multiaddr)).into(); 204 | } 205 | 206 | let particle = marine_rs_sdk::get_call_parameters().particle; 207 | let particle_vault_path = vault_path(&particle); 208 | let path = format!("{}/{}", particle_vault_path, hash); 209 | let get_result = ipfs_dag_get(hash, &path, external_multiaddr, timeout); 210 | 211 | if get_result.success { 212 | Ok(path).into() 213 | } else { 214 | Err(eyre::eyre!(get_result.error)).into() 215 | } 216 | } 217 | 218 | #[marine] 219 | pub fn cat(hash: String) -> IpfsCatResult { 220 | let local_maddr = load_local_api_multiaddr().map(|m| m.to_string()); 221 | if local_maddr.is_ok() { 222 | cat_from(hash, local_maddr.unwrap()) 223 | } else { 224 | local_maddr.into() 225 | } 226 | } 227 | 228 | #[marine] 229 | pub fn cat_from(hash: String, external_multiaddr: String) -> IpfsCatResult { 230 | let config = load_config(); 231 | let timeout = config.timeout; 232 | 233 | if Multiaddr::from_str(&external_multiaddr).is_err() { 234 | return Err(eyre::eyre!("invalid multiaddr: {}", external_multiaddr)).into(); 235 | } 236 | 237 | ipfs_cat(hash, external_multiaddr, timeout) 238 | } 239 | 240 | #[marine] 241 | pub fn get_external_api_multiaddr() -> IpfsMultiaddrResult { 242 | load_external_api_multiaddr().map(|m| m.to_string()).into() 243 | } 244 | 245 | #[marine] 246 | pub fn set_external_api_multiaddr(multiaddr: String) -> IpfsResult { 247 | let call_parameters = marine_rs_sdk::get_call_parameters(); 248 | if call_parameters.particle.init_peer_id != call_parameters.service_creator_peer_id { 249 | return eyre::Result::<()>::Err(eyre::eyre!( 250 | "only service creator can set external api multiaddr" 251 | )) 252 | .into(); 253 | } 254 | 255 | let config = load_config(); 256 | let timeout = config.timeout; 257 | 258 | let result: eyre::Result<()> = try { 259 | let mut multiaddr = Multiaddr::from_str(&multiaddr) 260 | .wrap_err(format!("invalid multiaddr: {}", multiaddr))?; 261 | let local_maddr = load_local_api_multiaddr()?.to_string(); 262 | let mut passed_peer_id = None; 263 | match multiaddr.iter().count() { 264 | 3 => { 265 | passed_peer_id = multiaddr.pop(); 266 | } 267 | 2 => {} 268 | n => Err(eyre::eyre!( 269 | "multiaddr should contain 2 or 3 components, {} given", 270 | n 271 | ))?, 272 | } 273 | 274 | let peer_id = get_peer_id(local_maddr, timeout)?; 275 | if passed_peer_id.is_some() && passed_peer_id != Some(peer_id.clone()) { 276 | Err(eyre::eyre!( 277 | "given peer id is different from node peer_id: given {}, actual {}", 278 | passed_peer_id.unwrap().to_string(), 279 | peer_id.to_string() 280 | ))?; 281 | } 282 | 283 | multiaddr.push(peer_id); 284 | save_external_api_multiaddr(multiaddr); 285 | () 286 | }; 287 | 288 | result.into() 289 | } 290 | 291 | #[marine] 292 | pub fn get_local_api_multiaddr() -> IpfsMultiaddrResult { 293 | load_local_api_multiaddr().map(|m| m.to_string()).into() 294 | } 295 | 296 | #[marine] 297 | pub fn set_local_api_multiaddr(multiaddr: String) -> IpfsResult { 298 | let call_parameters = marine_rs_sdk::get_call_parameters(); 299 | if call_parameters.particle.init_peer_id != call_parameters.service_creator_peer_id { 300 | return eyre::Result::<()>::Err(eyre::eyre!( 301 | "only service creator can set local api multiaddr" 302 | )) 303 | .into(); 304 | } 305 | 306 | let result: eyre::Result<()> = try { 307 | save_local_api_multiaddr( 308 | Multiaddr::from_str(&multiaddr) 309 | .wrap_err(format!("invalid multiaddr: {}", multiaddr))?, 310 | ) 311 | }; 312 | 313 | result.into() 314 | } 315 | 316 | #[marine] 317 | pub fn get_external_swarm_multiaddr() -> IpfsMultiaddrResult { 318 | load_config() 319 | .external_swarm_multiaddr 320 | .ok_or(eyre::eyre!("multiaddr is not set")) 321 | .map(|m| m.to_string()) 322 | .into() 323 | } 324 | 325 | #[marine] 326 | pub fn set_external_swarm_multiaddr(multiaddr: String) -> IpfsResult { 327 | let call_parameters = marine_rs_sdk::get_call_parameters(); 328 | if call_parameters.particle.init_peer_id != call_parameters.service_creator_peer_id { 329 | return eyre::Result::<()>::Err(eyre::eyre!( 330 | "only service creator can set external swarm multiaddr" 331 | )) 332 | .into(); 333 | } 334 | 335 | let result: eyre::Result<()> = try { 336 | let mut config = load_config(); 337 | 338 | let mut multiaddr = Multiaddr::from_str(&multiaddr) 339 | .wrap_err(format!("invalid multiaddr: {}", multiaddr))?; 340 | let local_maddr = load_local_api_multiaddr()?.to_string(); 341 | 342 | let mut passed_peer_id = None; 343 | match multiaddr.iter().count() { 344 | 3 => { 345 | passed_peer_id = multiaddr.pop(); 346 | } 347 | 2 => {} 348 | n => Err(eyre::eyre!( 349 | "multiaddr should contain 2 or 3 components, {} given", 350 | n 351 | ))?, 352 | } 353 | 354 | let peer_id = get_peer_id(local_maddr.clone(), config.timeout)?; 355 | if passed_peer_id.is_some() && passed_peer_id != Some(peer_id.clone()) { 356 | Err(eyre::eyre!( 357 | "given peer id is different from node peer_id: given {}, actual {}", 358 | passed_peer_id.unwrap().to_string(), 359 | peer_id.to_string() 360 | ))?; 361 | } 362 | 363 | multiaddr.push(peer_id); 364 | config.external_swarm_multiaddr = Some(multiaddr); 365 | write_config(config) 366 | }; 367 | 368 | result.into() 369 | } 370 | 371 | #[marine] 372 | pub fn set_timeout(timeout_sec: u64) { 373 | let mut config = load_config(); 374 | config.timeout = timeout_sec; 375 | write_config(config); 376 | } 377 | 378 | #[marine] 379 | #[module_import("ipfs_effector")] 380 | extern "C" { 381 | #[link_name = "connect"] 382 | pub fn ipfs_connect( 383 | external_multiaddr: String, 384 | api_multiaddr: String, 385 | timeout_sec: u64, 386 | ) -> IpfsResult; 387 | 388 | /// Put provided file to ipfs, return ipfs hash of the file. 389 | #[link_name = "put"] 390 | pub fn ipfs_put(file_path: String, api_multiaddr: String, timeout_sec: u64) -> IpfsPutResult; 391 | 392 | /// Put provided dag to ipfs, return ipfs hash of the dag. 393 | #[link_name = "dag_put"] 394 | pub fn ipfs_dag_put(dag: String, api_multiaddr: String, timeout_sec: u64) -> IpfsPutResult; 395 | 396 | /// Get file from ipfs by hash. 397 | #[link_name = "get"] 398 | pub fn ipfs_get( 399 | hash: String, 400 | file_path: &str, 401 | api_multiaddr: String, 402 | timeout_sec: u64, 403 | ) -> IpfsResult; 404 | 405 | // Get dag from ipfs by hash. 406 | #[link_name = "dag_get"] 407 | pub fn ipfs_dag_get( 408 | hash: String, 409 | file_path: &str, 410 | api_multiaddr: String, 411 | timeout_sec: u64, 412 | ) -> IpfsResult; 413 | 414 | /// Get file from ipfs by hash. 415 | #[link_name = "cat"] 416 | pub fn ipfs_cat(hash: String, api_multiaddr: String, timeout_sec: u64) -> IpfsCatResult; 417 | 418 | #[link_name = "get_peer_id"] 419 | pub fn ipfs_get_peer_id(local_multiaddr: String, timeout_sec: u64) -> IpfsGetPeerIdResult; 420 | } 421 | -------------------------------------------------------------------------------- /service/pure/tests/Config.toml: -------------------------------------------------------------------------------- 1 | modules_dir = "../../artifacts/" 2 | total_memory_limit = "Infinity" 3 | 4 | [[module]] 5 | name = "ipfs_effector" 6 | mem_pages_count = 100 7 | logger_enabled = true 8 | 9 | [module.mounted_binaries] 10 | ipfs = "../../effector/tests/ipfs" 11 | 12 | 13 | [[module]] 14 | name = "ipfs_pure" 15 | mem_pages_count = 100 16 | logger_enabled = true 17 | -------------------------------------------------------------------------------- /service/pure/tests/tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | marine_rs_sdk_test::include_test_env!("/marine_test_env.rs"); 20 | use marine_test_env::ipfs::ServiceInterface; 21 | 22 | fn set_default_local_api_multiaddr(ipfs: &mut ServiceInterface) { 23 | let result = ipfs.set_local_api_multiaddr("/ip4/127.0.0.1/tcp/9992".to_string()); 24 | assert!(result.success); 25 | } 26 | 27 | #[test] 28 | fn invalid_multiaddr() { 29 | let mut ipfs = ServiceInterface::new(); 30 | let invalid_multiaddr = "invalid_multiaddr".to_string(); 31 | let result = ipfs.set_local_api_multiaddr(invalid_multiaddr.clone()); 32 | assert!(!result.success); 33 | assert_eq!( 34 | format!("invalid multiaddr: {}", invalid_multiaddr), 35 | result.error 36 | ); 37 | } 38 | 39 | #[test] 40 | fn set_get_external_api_multiaddr() { 41 | let mut ipfs = ServiceInterface::new(); 42 | set_default_local_api_multiaddr(&mut ipfs); 43 | let multiaddr = "/ip4/127.0.0.1/tcp/9992"; 44 | let result = ipfs.set_external_api_multiaddr(multiaddr.to_string()); 45 | assert!(result.success); 46 | 47 | let peer_id = ipfs 48 | .modules 49 | .ipfs_effector 50 | .get_peer_id("/ip4/127.0.0.1/tcp/5001".to_string(), 0) 51 | .peer_id; 52 | 53 | let result = ipfs.get_external_api_multiaddr(); 54 | assert!(result.success); 55 | assert_eq!(format!("{}/p2p/{}", multiaddr, peer_id), result.multiaddr); 56 | } 57 | 58 | #[test] 59 | fn set_get_external_swarm_multiaddr() { 60 | let mut ipfs = ServiceInterface::new(); 61 | set_default_local_api_multiaddr(&mut ipfs); 62 | let multiaddr = "/ip4/127.0.0.1/tcp/9992"; 63 | let result = ipfs.set_external_swarm_multiaddr(multiaddr.to_string()); 64 | assert!(result.success); 65 | 66 | let peer_id = ipfs 67 | .modules 68 | .ipfs_effector 69 | .get_peer_id("/ip4/127.0.0.1/tcp/5001".to_string(), 0) 70 | .peer_id; 71 | 72 | let result = ipfs.get_external_swarm_multiaddr(); 73 | assert!(result.success); 74 | assert_eq!(format!("{}/p2p/{}", multiaddr, peer_id), result.multiaddr); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /service/types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "types" 3 | version = "0.6.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | marine-rs-sdk = { workspace = true } 11 | eyre = "0.6.8" 12 | -------------------------------------------------------------------------------- /service/types/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod results; 2 | 3 | pub use results::{ 4 | IpfsCatResult, IpfsGetPeerIdResult, IpfsGetResult, IpfsMultiaddrResult, IpfsPutResult, 5 | IpfsResult, 6 | }; 7 | -------------------------------------------------------------------------------- /service/types/src/results.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Fluence Labs Limited 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | use eyre::Result; 18 | use marine_rs_sdk::marine; 19 | 20 | #[marine] 21 | pub struct IpfsResult { 22 | pub success: bool, 23 | pub error: String, 24 | } 25 | 26 | impl From> for IpfsResult { 27 | fn from(result: Result<()>) -> Self { 28 | match result { 29 | Ok(_) => Self { 30 | success: true, 31 | error: "".to_string(), 32 | }, 33 | Err(err) => Self { 34 | success: false, 35 | error: err.to_string(), 36 | }, 37 | } 38 | } 39 | } 40 | 41 | #[marine] 42 | pub struct IpfsGetResult { 43 | pub success: bool, 44 | pub error: String, 45 | pub path: String, 46 | } 47 | 48 | impl From> for IpfsGetResult { 49 | fn from(result: Result) -> Self { 50 | match result { 51 | Ok(path) => Self { 52 | success: true, 53 | error: "".to_string(), 54 | path, 55 | }, 56 | Err(err) => Self { 57 | success: false, 58 | error: err.to_string(), 59 | path: "".to_string(), 60 | }, 61 | } 62 | } 63 | } 64 | 65 | #[marine] 66 | pub struct IpfsPutResult { 67 | pub success: bool, 68 | pub error: String, 69 | pub hash: String, 70 | } 71 | 72 | impl From> for IpfsPutResult { 73 | fn from(result: Result) -> Self { 74 | match result { 75 | Ok(hash) => Self { 76 | success: true, 77 | error: "".to_string(), 78 | hash, 79 | }, 80 | Err(err) => Self { 81 | success: false, 82 | error: err.to_string(), 83 | hash: "".to_string(), 84 | }, 85 | } 86 | } 87 | } 88 | 89 | #[marine] 90 | pub struct IpfsGetPeerIdResult { 91 | pub success: bool, 92 | pub error: String, 93 | pub peer_id: String, 94 | } 95 | 96 | impl From> for IpfsGetPeerIdResult { 97 | fn from(result: Result) -> Self { 98 | match result { 99 | Ok(peer_id) => Self { 100 | success: true, 101 | error: "".to_string(), 102 | peer_id, 103 | }, 104 | Err(err) => Self { 105 | success: false, 106 | error: err.to_string(), 107 | peer_id: "".to_string(), 108 | }, 109 | } 110 | } 111 | } 112 | 113 | #[marine] 114 | pub struct IpfsMultiaddrResult { 115 | pub success: bool, 116 | pub error: String, 117 | pub multiaddr: String, 118 | } 119 | 120 | impl From> for IpfsMultiaddrResult { 121 | fn from(result: Result) -> Self { 122 | match result { 123 | Ok(multiaddr) => Self { 124 | success: true, 125 | error: "".to_string(), 126 | multiaddr, 127 | }, 128 | Err(err) => Self { 129 | success: false, 130 | error: err.to_string(), 131 | multiaddr: "".to_string(), 132 | }, 133 | } 134 | } 135 | } 136 | 137 | #[marine] 138 | pub struct IpfsCatResult { 139 | pub success: bool, 140 | pub error: String, 141 | pub contents: String, 142 | } 143 | 144 | impl From> for IpfsCatResult { 145 | fn from(result: Result) -> Self { 146 | match result { 147 | Ok(contents) => Self { 148 | success: true, 149 | error: "".to_string(), 150 | contents, 151 | }, 152 | Err(err) => Self { 153 | success: false, 154 | error: err.to_string(), 155 | contents: "".to_string(), 156 | }, 157 | } 158 | } 159 | } 160 | --------------------------------------------------------------------------------