diff --git a/.github/workflows/CI-docker-gear.yml b/.github/workflows/CI-docker-gear.yml new file mode 100644 index 00000000000..69ec358f421 --- /dev/null +++ b/.github/workflows/CI-docker-gear.yml @@ -0,0 +1,49 @@ +name: CI | docker-gear release version + +on: + workflow_dispatch: + inputs: + release_version: + description: 'Release version from https://get.gear.rs Example: v1.0.0. *Null = latest' + required: false + default: '' + +env: + RELEASE_VERSION: ${{ github.event.inputs.release_version }} + +jobs: + build: + runs-on: [kuberunner] + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v2 + + - uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-gear-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-gear + + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - run: | + if [ -z "${{ env.RELEASE_VERSION }}" ]; then + echo "DOCKER_TAGS=ghcr.io/gear-tech/node:latest" >> $GITHUB_ENV + else + echo "DOCKER_TAGS=ghcr.io/gear-tech/node:latest,ghcr.io/gear-tech/node:${{ env.RELEASE_VERSION }}" >> $GITHUB_ENV + fi + + - uses: docker/build-push-action@v4 + with: + file: ./docker/Dockerfile-release + push: true + tags: ${{ env.DOCKER_TAGS }} + build-args: | + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index cdb98420dac..8ec90626b45 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -3,10 +3,15 @@ name: Benchmarks on: workflow_dispatch: inputs: - create_pr: - description: Whether to create a pull request after the benchmarks are completed - required: false - type: boolean + change-type: + description: How should changes be made after the benchmarks are completed? + required: true + default: ignore + type: choice + options: + - ignore + - commit + - pull_request env: CARGO_TERM_COLOR: always @@ -15,6 +20,9 @@ env: jobs: benchmarks: runs-on: bench + permissions: + contents: write + pull-requests: write env: RUSTUP_HOME: /tmp/rustup_home steps: @@ -65,11 +73,18 @@ jobs: run: | echo "::notice::You can download the artifacts and use script \`./scripts/unpack-weights.sh\` to apply the changes to your branch" - - name: Create Pull Request - if: ${{ inputs.create_pr }} + - name: Create commit + if: ${{ inputs.change-type == 'commit' }} + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: >- + chore(runtime): update weights + file_pattern: "pallets/gear/src/ runtime/gear/src/weights/ runtime/vara/src/weights/" + + - name: Create pull Request + if: ${{ inputs.change-type == 'pull_request' }} uses: peter-evans/create-pull-request@v5 with: - token: ${{ secrets.GH_TOKEN_FOR_PR }} add-paths: | pallets/gear/src/ runtime/gear/src/weights/ @@ -78,7 +93,7 @@ jobs: branch: weights/patch branch-suffix: short-commit-hash title: >- - chore: update weights + chore(runtime): update weights body: | `run_all_benchmarks.sh` script is applied to update weights (branch: `${{ github.ref_name }}`, commit ${{ github.sha }}) **Note:** If CI fails, try manually updating the heuristic tests: `runtime/{gear, vara}/src/tests.rs` diff --git a/.github/workflows/comparison-table.yml b/.github/workflows/comparison-table.yml new file mode 100644 index 00000000000..17f39581145 --- /dev/null +++ b/.github/workflows/comparison-table.yml @@ -0,0 +1,46 @@ +name: Comparison table + +on: + workflow_dispatch: + inputs: + runtime: + description: For which runtime generate comparison table? + required: true + type: choice + options: + - gear + - vara + +env: + CARGO_TERM_COLOR: always + TERM: xterm-256color + +jobs: + comparison-table: + runs-on: [kuberunner] + env: + RUSTUP_HOME: /tmp/rustup_home + steps: + - name: "ACTIONS: Checkout & fetch all history" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: "Install: Set cargo path" + run: echo "/tmp/cargo/bin" >> $GITHUB_PATH + + - name: "Install: Rust toolchain" + uses: dsherret/rust-toolchain-file@v1 + + - name: "Build: Init" + run: ./scripts/gear.sh init cargo + + - name: "Generate comparison tables (${{ inputs.runtime }})" + run: | + ./scripts/weight-diff.sh master $(git branch --show-current) ${{ inputs.runtime }} --display-units > ${{ inputs.runtime }}-tables.txt + + - name: "ACTIONS: Upload artifact with comparison tables (${{ inputs.runtime }})" + uses: actions/upload-artifact@v3 + with: + name: ${{ inputs.runtime }}-tables + path: ${{ inputs.runtime }}-tables.txt diff --git a/.github/workflows/time-consuming-tests.yml b/.github/workflows/time-consuming-tests.yml index 126744ebcfa..9c75a58480c 100644 --- a/.github/workflows/time-consuming-tests.yml +++ b/.github/workflows/time-consuming-tests.yml @@ -24,7 +24,7 @@ jobs: - name: "Install: Rust toolchain" uses: dsherret/rust-toolchain-file@v1 - - name: "Buid: Init" + - name: "Build: Init" run: ./scripts/gear.sh init cargo - name: "Build: Node" diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml new file mode 100644 index 00000000000..09ca0e73dc1 --- /dev/null +++ b/.github/workflows/validation.yml @@ -0,0 +1,69 @@ +name: Live check on Vara Network Validator machine + +on: + pull_request: + types: [synchronize, labeled, opened, reopened, ready_for_review] + branches: [master] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + tag-image: + runs-on: ubuntu-latest + if: contains(github.event.pull_request.labels.*.name, 'check-validator') + outputs: + image_tag: ${{ steps.image-tag.outputs.tag }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Create image tag + id: image-tag + run: echo "tag=ghcr.io/gear-tech/node:0.1.0-`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_OUTPUT + + build-update-validator: + runs-on: [kuberunner] + if: contains(github.event.pull_request.labels.*.name, 'check-validator') + needs: tag-image + steps: + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Echo tag + run: echo ${{ needs.tag-image.outputs.image_tag }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v4 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: ghcr.io/gear-tech/node:latest, ${{ needs.tag-image.outputs.image_tag }} + + - name: SSH into VM + uses: appleboy/ssh-action@v1.0.0 + env: + NEW_IMAGE: ${{ needs.tag-image.outputs.image_tag }} + with: + host: ${{ secrets.VARA_VALIDATOR_8 }} + username: ${{ secrets.SSH_VARA_USERNAME }} + key: ${{ secrets.VARA_SSH_PRIVATE_KEY }} + envs: NEW_IMAGE + script: | + sudo docker-compose -f /home/gear/docker-compose.yaml down + awk -v new_image="$NEW_IMAGE" '{gsub(/image: ghcr.io\/gear-tech\/node:.*/, "image: " new_image)}1' /home/gear/docker-compose.yaml > tmp && mv tmp /home/gear/docker-compose.yaml + sudo docker-compose -f /home/gear/docker-compose.yaml up -d diff --git a/Cargo.lock b/Cargo.lock index 3041ea8580e..bda8e9987c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2227,7 +2227,6 @@ dependencies = [ "gear-core", "gear-wasm-builder", "gstd", - "gsys", "gtest", "parity-scale-codec", ] @@ -2582,11 +2581,11 @@ checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "dlmalloc" version = "0.1.4" -source = "git+https://github.com/gear-tech/dlmalloc-rust.git?rev=9135baa#9135baa728ef9a9a04a887998e019733c4b093af" +source = "git+https://github.com/gear-tech/dlmalloc-rust.git#15352f969112faa463302f2490bbb7f6e1cb904d" dependencies = [ "libc", - "libc_print", - "page_size 0.4.2", + "libc-print", + "page_size 0.6.0", "static_assertions", "str-buf", ] @@ -3633,14 +3632,14 @@ dependencies = [ [[package]] name = "galloc" -version = "1.0.0" +version = "1.0.1" dependencies = [ "dlmalloc", ] [[package]] name = "gcli" -version = "1.0.0" +version = "1.0.1" dependencies = [ "anyhow", "base64 0.21.3", @@ -3681,7 +3680,7 @@ dependencies = [ [[package]] name = "gclient" -version = "1.0.0" +version = "1.0.1" dependencies = [ "anyhow", "async-trait", @@ -3725,7 +3724,7 @@ dependencies = [ [[package]] name = "gcore" -version = "1.0.0" +version = "1.0.1" dependencies = [ "galloc", "gear-core-errors", @@ -3738,7 +3737,7 @@ dependencies = [ [[package]] name = "gear-authorship" -version = "1.0.0" +version = "1.0.1" dependencies = [ "demo-mul-by-const", "env_logger", @@ -3754,6 +3753,7 @@ dependencies = [ "pallet-balances", "pallet-gear", "pallet-gear-messenger", + "pallet-gear-program", "pallet-gear-rpc-runtime-api", "pallet-sudo", "pallet-timestamp", @@ -3781,7 +3781,7 @@ dependencies = [ [[package]] name = "gear-backend-codegen" -version = "1.0.0" +version = "1.0.1" dependencies = [ "proc-macro2", "quote", @@ -3790,7 +3790,7 @@ dependencies = [ [[package]] name = "gear-backend-common" -version = "1.0.0" +version = "1.0.1" dependencies = [ "actor-system-error", "blake2-rfc", @@ -3849,7 +3849,7 @@ dependencies = [ [[package]] name = "gear-cli" -version = "1.0.0" +version = "1.0.1" dependencies = [ "clap 4.4.2", "frame-benchmarking", @@ -3883,7 +3883,7 @@ dependencies = [ [[package]] name = "gear-common" -version = "1.0.0" +version = "1.0.1" dependencies = [ "derive_more", "enum-iterator 1.4.1", @@ -3911,7 +3911,7 @@ dependencies = [ [[package]] name = "gear-common-codegen" -version = "1.0.0" +version = "1.0.1" dependencies = [ "quote", "syn 2.0.31", @@ -3919,7 +3919,7 @@ dependencies = [ [[package]] name = "gear-core" -version = "1.0.0" +version = "1.0.1" dependencies = [ "blake2-rfc", "byteorder", @@ -3935,6 +3935,7 @@ dependencies = [ "paste", "proptest", "scale-info", + "serde", "static_assertions", "wabt", "wasmparser-nostd 0.100.1", @@ -3942,7 +3943,7 @@ dependencies = [ [[package]] name = "gear-core-errors" -version = "1.0.0" +version = "1.0.1" dependencies = [ "derive_more", "enum-iterator 1.4.1", @@ -3951,7 +3952,7 @@ dependencies = [ [[package]] name = "gear-core-processor" -version = "1.0.0" +version = "1.0.1" dependencies = [ "actor-system-error", "derive_more", @@ -3969,7 +3970,7 @@ dependencies = [ [[package]] name = "gear-lazy-pages" -version = "1.0.0" +version = "1.0.1" dependencies = [ "cfg-if", "derive_more", @@ -4009,7 +4010,7 @@ dependencies = [ [[package]] name = "gear-node-loader" -version = "1.0.0" +version = "1.0.1" dependencies = [ "anyhow", "arbitrary", @@ -4041,7 +4042,7 @@ dependencies = [ [[package]] name = "gear-node-testing" -version = "1.0.0" +version = "1.0.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -4110,7 +4111,7 @@ dependencies = [ [[package]] name = "gear-runtime" -version = "1.0.0" +version = "1.0.1" dependencies = [ "const-str", "frame-benchmarking", @@ -4172,7 +4173,7 @@ dependencies = [ [[package]] name = "gear-runtime-common" -version = "1.0.0" +version = "1.0.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -4198,7 +4199,7 @@ dependencies = [ [[package]] name = "gear-runtime-interface" -version = "1.0.0" +version = "1.0.1" dependencies = [ "byteorder", "derive_more", @@ -4220,7 +4221,7 @@ dependencies = [ [[package]] name = "gear-runtime-primitives" -version = "1.0.0" +version = "1.0.1" dependencies = [ "sp-core 7.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", "sp-runtime 7.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", @@ -4273,7 +4274,7 @@ dependencies = [ [[package]] name = "gear-service" -version = "1.0.0" +version = "1.0.1" dependencies = [ "frame-benchmarking", "frame-benchmarking-cli", @@ -4354,7 +4355,7 @@ dependencies = [ [[package]] name = "gear-stack-buffer" -version = "1.0.0" +version = "1.0.1" dependencies = [ "cc", ] @@ -4448,7 +4449,7 @@ dependencies = [ [[package]] name = "gear-wasm-instrument" -version = "1.0.0" +version = "1.0.1" dependencies = [ "enum-iterator 1.4.1", "gear-backend-common", @@ -4618,7 +4619,7 @@ dependencies = [ [[package]] name = "gmeta" -version = "1.0.0" +version = "1.0.1" dependencies = [ "blake2-rfc", "derive_more", @@ -4632,7 +4633,7 @@ dependencies = [ [[package]] name = "gmeta-codegen" -version = "1.0.0" +version = "1.0.1" dependencies = [ "gmeta", "gstd", @@ -4656,7 +4657,7 @@ dependencies = [ [[package]] name = "gsdk" -version = "1.0.0" +version = "1.0.1" dependencies = [ "anyhow", "base64 0.21.3", @@ -4670,9 +4671,11 @@ dependencies = [ "gsdk", "gsdk-codegen", "hex", + "indexmap 2.0.0", "jsonrpsee", "log", "parity-scale-codec", + "parking_lot 0.12.1", "rand 0.8.5", "scale-decode", "scale-value", @@ -4707,7 +4710,7 @@ dependencies = [ [[package]] name = "gsdk-codegen" -version = "1.0.0" +version = "1.0.1" dependencies = [ "proc-macro2", "quote", @@ -4716,7 +4719,7 @@ dependencies = [ [[package]] name = "gstd" -version = "1.0.0" +version = "1.0.1" dependencies = [ "bs58", "futures", @@ -4745,7 +4748,7 @@ dependencies = [ [[package]] name = "gsys" -version = "1.0.0" +version = "1.0.1" [[package]] name = "gtest" @@ -5605,9 +5608,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "libc_print" -version = "0.1.16" -source = "git+https://github.com/grishasobol/rust-libc-print.git#b300804809e7a5f1c8fab4d2d11bcea29217bc70" +name = "libc-print" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06cea5d58bd9ba4717bbf5c6c5bb11bb6e9e76685b7fff34039b80f50ce86c11" dependencies = [ "libc", ] @@ -7114,9 +7118,9 @@ dependencies = [ [[package]] name = "page_size" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" dependencies = [ "libc", "winapi", @@ -7124,9 +7128,9 @@ dependencies = [ [[package]] name = "page_size" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" dependencies = [ "libc", "winapi", @@ -7313,7 +7317,7 @@ dependencies = [ [[package]] name = "pallet-gear" -version = "1.0.0" +version = "1.0.1" dependencies = [ "blake2-rfc", "demo-async", @@ -7411,7 +7415,7 @@ dependencies = [ [[package]] name = "pallet-gear-bank" -version = "1.0.0" +version = "1.0.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -7429,7 +7433,7 @@ dependencies = [ [[package]] name = "pallet-gear-debug" -version = "1.0.0" +version = "1.0.1" dependencies = [ "demo-vec", "env_logger", @@ -7466,7 +7470,7 @@ dependencies = [ [[package]] name = "pallet-gear-gas" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -7495,7 +7499,7 @@ dependencies = [ [[package]] name = "pallet-gear-messenger" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -7519,7 +7523,7 @@ dependencies = [ [[package]] name = "pallet-gear-payment" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -7553,7 +7557,7 @@ dependencies = [ [[package]] name = "pallet-gear-proc-macro" -version = "1.0.0" +version = "1.0.1" dependencies = [ "proc-macro2", "quote", @@ -7562,7 +7566,7 @@ dependencies = [ [[package]] name = "pallet-gear-program" -version = "1.0.0" +version = "1.0.1" dependencies = [ "frame-support", "frame-system", @@ -7587,7 +7591,7 @@ dependencies = [ [[package]] name = "pallet-gear-rpc" -version = "1.0.0" +version = "1.0.1" dependencies = [ "gear-common", "gear-core", @@ -7603,7 +7607,7 @@ dependencies = [ [[package]] name = "pallet-gear-rpc-runtime-api" -version = "1.0.0" +version = "1.0.1" dependencies = [ "pallet-gear", "sp-api", @@ -7614,7 +7618,7 @@ dependencies = [ [[package]] name = "pallet-gear-scheduler" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -7645,7 +7649,7 @@ dependencies = [ [[package]] name = "pallet-gear-staking-rewards" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -7697,7 +7701,7 @@ dependencies = [ [[package]] name = "pallet-gear-voucher" -version = "1.0.0" +version = "1.0.1" dependencies = [ "env_logger", "frame-benchmarking", @@ -12343,9 +12347,9 @@ dependencies = [ [[package]] name = "str-buf" -version = "2.0.5" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0873cb29201126440dcc78d0b1f5a13d917e78831778429a7920ca9c7f3dae1e" +checksum = "e75b72ee54e2f93c3ea1354066162be893ee5e25773ab743de3e088cecbb4f31" [[package]] name = "strsim" @@ -13494,7 +13498,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vara-runtime" -version = "1.0.0" +version = "1.0.1" dependencies = [ "const-str", "env_logger", @@ -13750,7 +13754,7 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" version = "0.16.0" -source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#2db6004e21aa014f411f8b575a99969284cff702" +source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#9942645b3891607e13f11f6444ca320ba36a6a1f" dependencies = [ "leb128", ] @@ -13837,7 +13841,7 @@ dependencies = [ [[package]] name = "wasm-smith" version = "0.11.4" -source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#2db6004e21aa014f411f8b575a99969284cff702" +source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#9942645b3891607e13f11f6444ca320ba36a6a1f" dependencies = [ "arbitrary", "flagset", @@ -14219,7 +14223,7 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wasmparser" version = "0.90.0" -source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#2db6004e21aa014f411f8b575a99969284cff702" +source = "git+https://github.com/gear-tech/wasm-tools.git?branch=gear-stable#9942645b3891607e13f11f6444ca320ba36a6a1f" dependencies = [ "indexmap 1.9.3", ] diff --git a/Cargo.toml b/Cargo.toml index eecdbf0432f..95f1b55982d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "1.0.0" +version = "1.0.1" authors = ["Gear Technologies"] edition = "2021" license = "GPL-3.0" @@ -105,7 +105,7 @@ colored = "2.0.0" const-str = "0.5" derive_more = "0.99.17" dirs = "4.0.0" -dlmalloc = { git = "https://github.com/gear-tech/dlmalloc-rust.git", rev = "9135baa", default-features = false } +dlmalloc = { git = "https://github.com/gear-tech/dlmalloc-rust.git" } dyn-clonable = "0.9.0" enum-iterator = "1.4.0" env_logger = "0.10" diff --git a/core/Cargo.toml b/core/Cargo.toml index 666e400433f..6fb6635d08e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -26,10 +26,15 @@ paste = { workspace = true } enum-iterator.workspace = true byteorder.workspace = true +# Optional dependencies +serde = { workspace = true, features = [ "derive" ], optional = true } + [dev-dependencies] wabt.workspace = true env_logger.workspace = true proptest.workspace = true [features] +default = [] strict = [] +std = ["serde/std"] diff --git a/core/src/gas.rs b/core/src/gas.rs index 8cfe903e66b..022d10116d3 100644 --- a/core/src/gas.rs +++ b/core/src/gas.rs @@ -20,7 +20,10 @@ use crate::costs::RuntimeCosts; use enum_iterator::Sequence; -use scale_info::scale::{Decode, Encode}; +use scale_info::{ + scale::{Decode, Encode}, + TypeInfo, +}; /// The id of the gas lock. #[derive(Debug, Clone, Copy, PartialEq, Eq, Sequence)] @@ -329,6 +332,27 @@ impl From<(i64, i64)> for GasLeft { } } +/// The struct contains results of gas calculation required to process +/// a message. +#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct GasInfo { + /// Represents minimum gas limit required for execution. + pub min_limit: u64, + /// Gas amount that we reserve for some other on-chain interactions. + pub reserved: u64, + /// Contains number of gas burned during message processing. + pub burned: u64, + /// The value may be returned if a program happens to be executed + /// the second or next time in a block. + pub may_be_returned: u64, + /// Was the message placed into waitlist at the end of calculating. + /// + /// This flag shows, that `min_limit` makes sense and have some guarantees + /// only before insertion into waitlist. + pub waited: bool, +} + #[cfg(test)] mod tests { use super::{ChargeResult, GasCounter}; diff --git a/docker/Dockerfile b/docker/Dockerfile index a36437d090c..c15e2bd8d13 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -37,7 +37,7 @@ RUN cargo build -p gear-cli --profile $PROFILE # ===== SECOND STAGE ====== -FROM ubuntu:22.10 +FROM ubuntu:22.04 MAINTAINER GEAR LABEL description="This is the 2nd stage: a very small image where we copy the Gear binary." ARG PROFILE=production diff --git a/docker/Dockerfile-release b/docker/Dockerfile-release new file mode 100644 index 00000000000..d5181c2092c --- /dev/null +++ b/docker/Dockerfile-release @@ -0,0 +1,17 @@ +FROM debian:12-slim + +ARG RELEASE_VERSION + +RUN apt-get update && \ + apt-get install -y curl sudo xz-utils && \ + rm -rf /var/lib/apt/lists/* + +RUN if [ -z "$RELEASE_VERSION" ]; then \ + curl --proto '=https' --tlsv1.2 -sSf https://get.gear.rs/install.sh | bash -s -- --to /usr/local/bin/ ; \ + else \ + curl --proto '=https' --tlsv1.2 -sSf https://get.gear.rs/install.sh | bash -s -- --tag ${RELEASE_VERSION} --to /usr/local/bin/ ; \ + fi + +RUN gear --version + +CMD ["gear"] diff --git a/examples/async-custom-entry/Cargo.toml b/examples/async-custom-entry/Cargo.toml index 7a059c0d776..ce820c11930 100644 --- a/examples/async-custom-entry/Cargo.toml +++ b/examples/async-custom-entry/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-async-custom-entry" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -16,8 +17,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/async-custom-entry/src/lib.rs b/examples/async-custom-entry/src/lib.rs index 6a7b7d54ea0..b20b99554f5 100644 --- a/examples/async-custom-entry/src/lib.rs +++ b/examples/async-custom-entry/src/lib.rs @@ -27,33 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, ActorId}; - - static mut USER: ActorId = ActorId::zero(); - - #[gstd::async_init(handle_reply = my_handle_reply, handle_signal = my_handle_signal)] - async fn init() { - gstd::Config::set_system_reserve(10_000_000_000).expect("Failed to set system reserve"); - - unsafe { USER = msg::source() } - } - - #[gstd::async_main] - async fn main() { - #[allow(clippy::empty_loop)] - loop {} - } - - fn my_handle_reply() { - unsafe { - msg::send_bytes(USER, b"my_handle_reply", 0).unwrap(); - } - } - - fn my_handle_signal() { - unsafe { - msg::send_bytes(USER, b"my_handle_signal", 0).unwrap(); - } - } -} +mod wasm; diff --git a/examples/async-custom-entry/src/wasm.rs b/examples/async-custom-entry/src/wasm.rs new file mode 100644 index 00000000000..a8f110f5bb2 --- /dev/null +++ b/examples/async-custom-entry/src/wasm.rs @@ -0,0 +1,46 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{msg, ActorId}; + +static mut USER: ActorId = ActorId::zero(); + +#[gstd::async_init(handle_reply = my_handle_reply, handle_signal = my_handle_signal)] +async fn init() { + gstd::Config::set_system_reserve(10_000_000_000).expect("Failed to set system reserve"); + + unsafe { USER = msg::source() } +} + +#[gstd::async_main] +async fn main() { + #[allow(clippy::empty_loop)] + loop {} +} + +fn my_handle_reply() { + unsafe { + msg::send_bytes(USER, b"my_handle_reply", 0).unwrap(); + } +} + +fn my_handle_signal() { + unsafe { + msg::send_bytes(USER, b"my_handle_signal", 0).unwrap(); + } +} diff --git a/examples/async-init/Cargo.toml b/examples/async-init/Cargo.toml index 950e247e59d..eb2eb78d4b6 100644 --- a/examples/async-init/Cargo.toml +++ b/examples/async-init/Cargo.toml @@ -4,12 +4,13 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } -futures = { workspace = true, features = ["alloc"] } +parity-scale-codec.workspace = true +futures.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/async-init/src/lib.rs b/examples/async-init/src/lib.rs index d5111c026cd..f58aab75c33 100644 --- a/examples/async-init/src/lib.rs +++ b/examples/async-init/src/lib.rs @@ -57,74 +57,4 @@ impl InputArgs { } #[cfg(not(feature = "std"))] -mod wasm { - /* The program demonstrates asynchronous execution and - * how to use macros `gstd::async_init`/`gstd::async_main`. - * - * `Init` method gets three addresses, sends "PING" messages - * to them and waits for at least two replies with any payload ("approvals"). - * - * `Handle` processes only "PING" messages. When `handle` gets such message - * it sends empty requests to the three addresses and waits for just one approval. - * If an approval is obtained the method replies with "PONG". - */ - - use crate::InputArgs; - use futures::future; - use gstd::{msg, prelude::*, ActorId}; - - // One of the addresses supposed to be non-program. - static mut ARGUMENTS: InputArgs = InputArgs { - approver_first: ActorId::zero(), - approver_second: ActorId::zero(), - approver_third: ActorId::zero(), - }; - - static mut RESPONSES: u8 = 0; - - #[gstd::async_init] - async fn init() { - let arguments: InputArgs = msg::load().expect("Failed to load arguments"); - - let mut requests = arguments - .iter() - .map(|&addr| { - msg::send_bytes_for_reply(addr, "PING", 0, 0).expect("Failed to send message") - }) - .collect::>(); - - unsafe { - ARGUMENTS = arguments; - } - - while !requests.is_empty() { - let (.., remaining) = future::select_all(requests).await; - unsafe { - RESPONSES += 1; - } - - if unsafe { RESPONSES } >= 2 { - break; - } - - requests = remaining; - } - } - - #[gstd::async_main] - async fn main() { - let message = msg::load_bytes().expect("Failed to load bytes"); - - assert_eq!(message, b"PING"); - - let requests = unsafe { ARGUMENTS.iter() } - .map(|&addr| { - msg::send_bytes_for_reply(addr, "PING", 0, 0).expect("Failed to send message") - }) - .collect::>(); - - let _ = future::select_all(requests).await; - - msg::reply(unsafe { RESPONSES }, 0).expect("Failed to send reply"); - } -} +mod wasm; diff --git a/examples/async-init/src/wasm.rs b/examples/async-init/src/wasm.rs new file mode 100644 index 00000000000..0b12705ca91 --- /dev/null +++ b/examples/async-init/src/wasm.rs @@ -0,0 +1,82 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! The program demonstrates asynchronous execution and +//! how to use macros `gstd::async_init`/`gstd::async_main`. +//! +//! `Init` method gets three addresses, sends "PING" messages +//! to them and waits for at least two replies with any payload ("approvals"). +//! +//! `Handle` processes only "PING" messages. When `handle` gets such message +//! it sends empty requests to the three addresses and waits for just one approval. +//! If an approval is obtained the method replies with "PONG". + +use crate::InputArgs; +use futures::future; +use gstd::{msg, prelude::*, ActorId}; + +// One of the addresses supposed to be non-program. +static mut ARGUMENTS: InputArgs = InputArgs { + approver_first: ActorId::zero(), + approver_second: ActorId::zero(), + approver_third: ActorId::zero(), +}; + +static mut RESPONSES: u8 = 0; + +#[gstd::async_init] +async fn init() { + let arguments: InputArgs = msg::load().expect("Failed to load arguments"); + + let mut requests = arguments + .iter() + .map(|&addr| msg::send_bytes_for_reply(addr, "PING", 0, 0).expect("Failed to send message")) + .collect::>(); + + unsafe { + ARGUMENTS = arguments; + } + + while !requests.is_empty() { + let (.., remaining) = future::select_all(requests).await; + unsafe { + RESPONSES += 1; + } + + if unsafe { RESPONSES } >= 2 { + break; + } + + requests = remaining; + } +} + +#[gstd::async_main] +async fn main() { + let message = msg::load_bytes().expect("Failed to load bytes"); + + assert_eq!(message, b"PING"); + + let requests = unsafe { ARGUMENTS.iter() } + .map(|&addr| msg::send_bytes_for_reply(addr, "PING", 0, 0).expect("Failed to send message")) + .collect::>(); + + let _ = future::select_all(requests).await; + + msg::reply(unsafe { RESPONSES }, 0).expect("Failed to send reply"); +} diff --git a/examples/async-recursion/Cargo.toml b/examples/async-recursion/Cargo.toml index 8bdf01eb582..99fc3c4f579 100644 --- a/examples/async-recursion/Cargo.toml +++ b/examples/async-recursion/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/async-recursion/src/lib.rs b/examples/async-recursion/src/lib.rs index 7d32db1717b..fbc08d47a0b 100644 --- a/examples/async-recursion/src/lib.rs +++ b/examples/async-recursion/src/lib.rs @@ -27,39 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use async_recursion::async_recursion; - use gstd::{msg, prelude::*, ActorId}; - - static mut DESTINATION: ActorId = ActorId::zero(); - - #[no_mangle] - extern "C" fn init() { - let destination = msg::load().expect("Failed to load destination"); - unsafe { DESTINATION = destination }; - } - - /// Send message "PING" and wait for a reply, then recursively - /// repeat with `val` decreased by reply len while `val` > reply len. - #[async_recursion] - async fn rec_func(val: i32) { - let reply = msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) - .expect("Failed to send message") - .await - .expect("Received error reply"); - - msg::send(msg::source(), val, 0).expect("Failed to send message"); - - let reply_len = reply.len() as i32; - - if val - reply_len > 0 { - rec_func(val - reply_len).await; - } - } - - #[gstd::async_main] - async fn main() { - let arg = msg::load().expect("Failed to load argument"); - rec_func(arg).await; - } -} +mod wasm; diff --git a/examples/async-recursion/src/wasm.rs b/examples/async-recursion/src/wasm.rs new file mode 100644 index 00000000000..40648a95b08 --- /dev/null +++ b/examples/async-recursion/src/wasm.rs @@ -0,0 +1,52 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use async_recursion::async_recursion; +use gstd::{msg, prelude::*, ActorId}; + +static mut DESTINATION: ActorId = ActorId::zero(); + +#[no_mangle] +extern "C" fn init() { + let destination = msg::load().expect("Failed to load destination"); + unsafe { DESTINATION = destination }; +} + +/// Send message "PING" and wait for a reply, then recursively +/// repeat with `val` decreased by reply len while `val` > reply len. +#[async_recursion] +async fn rec_func(val: i32) { + let reply = msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) + .expect("Failed to send message") + .await + .expect("Received error reply"); + + msg::send(msg::source(), val, 0).expect("Failed to send message"); + + let reply_len = reply.len() as i32; + + if val - reply_len > 0 { + rec_func(val - reply_len).await; + } +} + +#[gstd::async_main] +async fn main() { + let arg = msg::load().expect("Failed to load argument"); + rec_func(arg).await; +} diff --git a/examples/async-signal-entry/Cargo.toml b/examples/async-signal-entry/Cargo.toml index e60c5baaceb..66067c35b43 100644 --- a/examples/async-signal-entry/Cargo.toml +++ b/examples/async-signal-entry/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-async-signal-entry" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -16,8 +17,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/async-signal-entry/src/lib.rs b/examples/async-signal-entry/src/lib.rs index 921725da3ef..a6e3c737ab1 100644 --- a/examples/async-signal-entry/src/lib.rs +++ b/examples/async-signal-entry/src/lib.rs @@ -35,28 +35,4 @@ pub enum InitAction { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gstd::{exec, msg}; - - #[gstd::async_init] - async fn init() { - let action = msg::load().unwrap(); - match action { - InitAction::None => {} - InitAction::Panic => { - let _bytes = msg::send_for_reply(msg::source(), b"init", 0, 0) - .unwrap() - .await - .unwrap(); - panic!(); - } - } - } - - #[gstd::async_main] - async fn main() { - msg::send(msg::source(), b"handle_signal", 0).unwrap(); - exec::wait(); - } -} +mod wasm; diff --git a/examples/async-signal-entry/src/wasm.rs b/examples/async-signal-entry/src/wasm.rs new file mode 100644 index 00000000000..a9192d60fd4 --- /dev/null +++ b/examples/async-signal-entry/src/wasm.rs @@ -0,0 +1,41 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::InitAction; +use gstd::{exec, msg}; + +#[gstd::async_init] +async fn init() { + let action = msg::load().unwrap(); + match action { + InitAction::None => {} + InitAction::Panic => { + let _bytes = msg::send_for_reply(msg::source(), b"init", 0, 0) + .unwrap() + .await + .unwrap(); + panic!(); + } + } +} + +#[gstd::async_main] +async fn main() { + msg::send(msg::source(), b"handle_signal", 0).unwrap(); + exec::wait(); +} diff --git a/examples/async-tester/Cargo.toml b/examples/async-tester/Cargo.toml index 114ec3a62a3..4e75128315a 100644 --- a/examples/async-tester/Cargo.toml +++ b/examples/async-tester/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-async-tester" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/async-tester/src/lib.rs b/examples/async-tester/src/lib.rs index 41822dd4176..d752d462007 100644 --- a/examples/async-tester/src/lib.rs +++ b/examples/async-tester/src/lib.rs @@ -10,9 +10,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; #[derive(Clone, Copy, Debug, Decode, Encode, PartialEq, Eq)] pub enum Kind { diff --git a/examples/async-tester/src/code.rs b/examples/async-tester/src/wasm.rs similarity index 91% rename from examples/async-tester/src/code.rs rename to examples/async-tester/src/wasm.rs index 3cad9d5052f..fe32ce5f66e 100644 --- a/examples/async-tester/src/code.rs +++ b/examples/async-tester/src/wasm.rs @@ -36,14 +36,16 @@ async fn main() { Kind::SendCommit => { let handle = MessageHandle::init().expect("init message failed"); handle.push(&encoded_kind).expect("push payload failed"); - handle.commit_for_reply(msg::source(), 0, 0) + handle + .commit_for_reply(msg::source(), 0, 0) .expect("send message failed") .await } Kind::SendCommitWithGas(gas) => { let handle = MessageHandle::init().expect("init message failed"); handle.push(&encoded_kind).expect("push payload failed"); - handle.commit_with_gas_for_reply(msg::source(), gas, 0, 0) + handle + .commit_with_gas_for_reply(msg::source(), gas, 0, 0) .expect("send message failed") .await } diff --git a/examples/async/Cargo.toml b/examples/async/Cargo.toml index be01a96165c..45a6b2ddb94 100644 --- a/examples/async/Cargo.toml +++ b/examples/async/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/async/src/lib.rs b/examples/async/src/lib.rs index c193368d6d4..113fcda6c27 100644 --- a/examples/async/src/lib.rs +++ b/examples/async/src/lib.rs @@ -35,50 +35,4 @@ pub enum Command { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::Command; - use gstd::{msg, prelude::*, sync::Mutex, ActorId}; - - static mut DESTINATION: ActorId = ActorId::zero(); - static MUTEX: Mutex = Mutex::new(0); - - #[no_mangle] - extern "C" fn init() { - let destination = msg::load().expect("Failed to load destination"); - unsafe { DESTINATION = destination }; - } - - async fn ping() -> Vec { - msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) - .expect("Failed to send message") - .await - .expect("Received error reply") - } - - #[gstd::async_main] - async fn main() { - let command = msg::load().expect("Failed to load command"); - - match command { - Command::Common => { - let r1 = ping().await; - let r2 = ping().await; - let r3 = ping().await; - - assert_eq!(r1, b"PONG"); - assert_eq!(r1, r2); - assert_eq!(r2, r3); - } - Command::Mutex => { - let _val = MUTEX.lock().await; - - msg::send(msg::source(), msg::id(), 0).expect("Failed to send message"); - let r = ping().await; - - assert_eq!(r, b"PONG"); - } - } - - msg::reply(msg::id(), 0).expect("Failed to send reply"); - } -} +mod wasm; diff --git a/examples/async/src/wasm.rs b/examples/async/src/wasm.rs new file mode 100644 index 00000000000..1b12121d16f --- /dev/null +++ b/examples/async/src/wasm.rs @@ -0,0 +1,63 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Command; +use gstd::{msg, prelude::*, sync::Mutex, ActorId}; + +static mut DESTINATION: ActorId = ActorId::zero(); +static MUTEX: Mutex = Mutex::new(0); + +#[no_mangle] +extern "C" fn init() { + let destination = msg::load().expect("Failed to load destination"); + unsafe { DESTINATION = destination }; +} + +async fn ping() -> Vec { + msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) + .expect("Failed to send message") + .await + .expect("Received error reply") +} + +#[gstd::async_main] +async fn main() { + let command = msg::load().expect("Failed to load command"); + + match command { + Command::Common => { + let r1 = ping().await; + let r2 = ping().await; + let r3 = ping().await; + + assert_eq!(r1, b"PONG"); + assert_eq!(r1, r2); + assert_eq!(r2, r3); + } + Command::Mutex => { + let _val = MUTEX.lock().await; + + msg::send(msg::source(), msg::id(), 0).expect("Failed to send message"); + let r = ping().await; + + assert_eq!(r, b"PONG"); + } + } + + msg::reply(msg::id(), 0).expect("Failed to send reply"); +} diff --git a/examples/autoreply/src/lib.rs b/examples/autoreply/src/lib.rs index 90a59205f62..0c98a3a8179 100644 --- a/examples/autoreply/src/lib.rs +++ b/examples/autoreply/src/lib.rs @@ -18,6 +18,8 @@ #![no_std] +extern crate alloc; + #[cfg(feature = "std")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -31,6 +33,7 @@ mod wasm; #[cfg(test)] mod tests { + use alloc::vec::Vec; use gstd::ActorId; use gtest::{Program, System}; @@ -58,7 +61,9 @@ mod tests { assert!(!res.main_failed()); // Check whether the auto-reply was received - let reply_received: bool = prog2.read_state().expect("Failed to read state"); + let reply_received: bool = prog2 + .read_state(Vec::::default()) + .expect("Failed to read state"); assert!(reply_received); } } diff --git a/examples/calc-hash/Cargo.toml b/examples/calc-hash/Cargo.toml index b61fb33ecf3..41a8bd8947a 100644 --- a/examples/calc-hash/Cargo.toml +++ b/examples/calc-hash/Cargo.toml @@ -2,12 +2,13 @@ name = "demo-calc-hash" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"]} +parity-scale-codec.workspace = true sha2 = { version = "0.10.6", default-features = false } [build-dependencies] diff --git a/examples/calc-hash/in-one-block/Cargo.toml b/examples/calc-hash/in-one-block/Cargo.toml index fcd0da368bb..7192e5baca9 100644 --- a/examples/calc-hash/in-one-block/Cargo.toml +++ b/examples/calc-hash/in-one-block/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-calc-hash-in-one-block" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"]} gstd.workspace = true +parity-scale-codec.workspace = true shared = { path = "..", package = "demo-calc-hash" } [build-dependencies] diff --git a/examples/calc-hash/in-one-block/src/lib.rs b/examples/calc-hash/in-one-block/src/lib.rs index fafd39d6b22..1d609b29753 100644 --- a/examples/calc-hash/in-one-block/src/lib.rs +++ b/examples/calc-hash/in-one-block/src/lib.rs @@ -27,9 +27,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; /// Package with expected #[derive(Encode, Decode)] diff --git a/examples/calc-hash/in-one-block/src/code.rs b/examples/calc-hash/in-one-block/src/wasm.rs similarity index 100% rename from examples/calc-hash/in-one-block/src/code.rs rename to examples/calc-hash/in-one-block/src/wasm.rs diff --git a/examples/calc-hash/over-blocks/Cargo.toml b/examples/calc-hash/over-blocks/Cargo.toml index f3795a7d0b0..5d93098fe37 100644 --- a/examples/calc-hash/over-blocks/Cargo.toml +++ b/examples/calc-hash/over-blocks/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-calc-hash-over-blocks" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"]} gstd.workspace = true +parity-scale-codec.workspace = true shared = { path = "../", package = "demo-calc-hash" } [build-dependencies] diff --git a/examples/calc-hash/over-blocks/src/lib.rs b/examples/calc-hash/over-blocks/src/lib.rs index 7c0054dd056..ab716fcd2da 100644 --- a/examples/calc-hash/over-blocks/src/lib.rs +++ b/examples/calc-hash/over-blocks/src/lib.rs @@ -29,9 +29,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; /// Program methods. #[derive(Debug, Encode, Decode)] diff --git a/examples/calc-hash/over-blocks/src/code.rs b/examples/calc-hash/over-blocks/src/wasm.rs similarity index 96% rename from examples/calc-hash/over-blocks/src/code.rs rename to examples/calc-hash/over-blocks/src/wasm.rs index 2d304ce3493..2cdc19f8526 100644 --- a/examples/calc-hash/over-blocks/src/code.rs +++ b/examples/calc-hash/over-blocks/src/wasm.rs @@ -15,7 +15,9 @@ extern "C" fn handle() { match method { Method::Start { expected, id, src } => { - registry.entry(id).or_insert_with(|| Package::new(expected, src)); + registry + .entry(id) + .or_insert_with(|| Package::new(expected, src)); let pkg = registry.get(&id).expect("Calculation not found."); diff --git a/examples/compose/Cargo.toml b/examples/compose/Cargo.toml index ed7edbd9bda..bd4a1891822 100644 --- a/examples/compose/Cargo.toml +++ b/examples/compose/Cargo.toml @@ -2,19 +2,18 @@ name = "demo-compose" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/compose/src/lib.rs b/examples/compose/src/lib.rs index 442e6d7ea0f..cc2e84c6fc0 100644 --- a/examples/compose/src/lib.rs +++ b/examples/compose/src/lib.rs @@ -34,111 +34,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - extern crate alloc; - - use gstd::{debug, exec, msg, prelude::*, ActorId}; - - static mut STATE: State = State { - contract_a: Program { - handle: ActorId::new([0u8; 32]), - }, - contract_b: Program { - handle: ActorId::new([0u8; 32]), - }, - }; - - struct State { - contract_a: Program, - contract_b: Program, - } - - impl State { - fn new(actor_a: impl Into, actor_b: impl Into) -> Self { - Self { - contract_a: Program::new(actor_a), - contract_b: Program::new(actor_b), - } - } - - async fn compose(&mut self, input: Vec) -> Result, &'static str> { - debug!( - "[0x{} compose::compose] Composing programs 0x{} and 0x{} on input {input:?}", - hex::encode(exec::program_id()), - hex::encode(self.contract_a.handle), - hex::encode(self.contract_b.handle), - ); - debug!( - "[0x{} compose::compose] Calling contract #1 at 0x{}", - hex::encode(exec::program_id()), - hex::encode(self.contract_a.handle) - ); - let output_a = self.contract_a.call(input).await?; - debug!( - "[0x{} compose::compose] Calling contract #2 at 0x{}", - hex::encode(exec::program_id()), - hex::encode(self.contract_b.handle) - ); - let output = self.contract_b.call(output_a).await?; - debug!( - "[0x{} compose::compose] Composition output: {output:?}", - hex::encode(exec::program_id()), - ); - - Ok(output) - } - } - - #[derive(Eq, Ord, PartialEq, PartialOrd)] - struct Program { - handle: ActorId, - } - - impl Program { - fn new(handle: impl Into) -> Self { - Self { - handle: handle.into(), - } - } - - async fn call(&self, input: Vec) -> Result, &'static str> { - let reply_bytes = msg::send_bytes_for_reply(self.handle, &input[..], 0, 0) - .expect("Error sending message") - .await - .map_err(|_| "Error in async message processing")?; - debug!( - "[0x{} compose::Program::call] Received reply from remote contract: {}", - hex::encode(exec::program_id()), - hex::encode(&reply_bytes) - ); - - Ok(reply_bytes) - } - } - - #[gstd::async_main] - async fn main() { - let input = msg::load_bytes().expect("Failed to load payload bytes"); - debug!( - "[0x{} compose::handle] input = {input:?}, gas_available = {}", - hex::encode(exec::program_id()), - exec::gas_available() - ); - - if let Ok(outcome) = (unsafe { STATE.compose(input) }).await { - debug!( - "[0x{} compose::handle] Composition output: {outcome:?}", - hex::encode(exec::program_id()), - ); - msg::reply(outcome, 0).unwrap(); - } - } - - #[no_mangle] - extern "C" fn init() { - let (contract_a, contract_b): (ActorId, ActorId) = - msg::load().expect("Expecting two contract addresses"); - unsafe { STATE = State::new(contract_a, contract_b) }; - msg::reply_bytes([], 0).unwrap(); - } -} +mod wasm; diff --git a/examples/compose/src/wasm.rs b/examples/compose/src/wasm.rs new file mode 100644 index 00000000000..a9c9c70a9a1 --- /dev/null +++ b/examples/compose/src/wasm.rs @@ -0,0 +1,131 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! This contract recursively composes itself with another contract (the other contract +//! being applied to the input data first): `c(f) = (c(f) . f) x`. +//! Every call to the auto_composer contract increments the internal `ITER` counter. +//! As soon as the counter reaches the `MAX_ITER`, the recursion stops. +//! Effectively, this procedure executes a composition of `MAX_ITER` contracts `f` +//! where the output of the previous call is fed to the input of the next call. + +extern crate alloc; + +use gstd::{debug, exec, msg, prelude::*, ActorId}; + +static mut STATE: State = State { + contract_a: Program { + handle: ActorId::new([0u8; 32]), + }, + contract_b: Program { + handle: ActorId::new([0u8; 32]), + }, +}; + +struct State { + contract_a: Program, + contract_b: Program, +} + +impl State { + fn new(actor_a: impl Into, actor_b: impl Into) -> Self { + Self { + contract_a: Program::new(actor_a), + contract_b: Program::new(actor_b), + } + } + + async fn compose(&mut self, input: Vec) -> Result, &'static str> { + debug!( + "[0x{} compose::compose] Composing programs 0x{} and 0x{} on input {input:?}", + hex::encode(exec::program_id()), + hex::encode(self.contract_a.handle), + hex::encode(self.contract_b.handle), + ); + debug!( + "[0x{} compose::compose] Calling contract #1 at 0x{}", + hex::encode(exec::program_id()), + hex::encode(self.contract_a.handle) + ); + let output_a = self.contract_a.call(input).await?; + debug!( + "[0x{} compose::compose] Calling contract #2 at 0x{}", + hex::encode(exec::program_id()), + hex::encode(self.contract_b.handle) + ); + let output = self.contract_b.call(output_a).await?; + debug!( + "[0x{} compose::compose] Composition output: {output:?}", + hex::encode(exec::program_id()), + ); + + Ok(output) + } +} + +#[derive(Eq, Ord, PartialEq, PartialOrd)] +struct Program { + handle: ActorId, +} + +impl Program { + fn new(handle: impl Into) -> Self { + Self { + handle: handle.into(), + } + } + + async fn call(&self, input: Vec) -> Result, &'static str> { + let reply_bytes = msg::send_bytes_for_reply(self.handle, &input[..], 0, 0) + .expect("Error sending message") + .await + .map_err(|_| "Error in async message processing")?; + debug!( + "[0x{} compose::Program::call] Received reply from remote contract: {}", + hex::encode(exec::program_id()), + hex::encode(&reply_bytes) + ); + + Ok(reply_bytes) + } +} + +#[gstd::async_main] +async fn main() { + let input = msg::load_bytes().expect("Failed to load payload bytes"); + debug!( + "[0x{} compose::handle] input = {input:?}, gas_available = {}", + hex::encode(exec::program_id()), + exec::gas_available() + ); + + if let Ok(outcome) = (unsafe { STATE.compose(input) }).await { + debug!( + "[0x{} compose::handle] Composition output: {outcome:?}", + hex::encode(exec::program_id()), + ); + msg::reply(outcome, 0).unwrap(); + } +} + +#[no_mangle] +extern "C" fn init() { + let (contract_a, contract_b): (ActorId, ActorId) = + msg::load().expect("Expecting two contract addresses"); + unsafe { STATE = State::new(contract_a, contract_b) }; + msg::reply_bytes([], 0).unwrap(); +} diff --git a/examples/constructor/Cargo.toml b/examples/constructor/Cargo.toml index 383ed6e29f7..093e3a2b005 100644 --- a/examples/constructor/Cargo.toml +++ b/examples/constructor/Cargo.toml @@ -4,13 +4,14 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gcore.workspace = true gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } hex.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/custom/Cargo.toml b/examples/custom/Cargo.toml index 164f8415084..bcf618a3c63 100644 --- a/examples/custom/Cargo.toml +++ b/examples/custom/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/delayed-sender/Cargo.toml b/examples/delayed-sender/Cargo.toml index 7c386c1b22a..199aaa4efa4 100644 --- a/examples/delayed-sender/Cargo.toml +++ b/examples/delayed-sender/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-delayed-sender" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/delayed-sender/src/lib.rs b/examples/delayed-sender/src/lib.rs index 1175ebd0d91..a2bb360b9a4 100644 --- a/examples/delayed-sender/src/lib.rs +++ b/examples/delayed-sender/src/lib.rs @@ -27,6 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; diff --git a/examples/delayed-sender/src/code.rs b/examples/delayed-sender/src/wasm.rs similarity index 89% rename from examples/delayed-sender/src/code.rs rename to examples/delayed-sender/src/wasm.rs index c1c1c2d61f8..395f2e7e03e 100644 --- a/examples/delayed-sender/src/code.rs +++ b/examples/delayed-sender/src/wasm.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use gstd::{msg, MessageId, exec}; +use gstd::{exec, msg, MessageId}; static mut MID: Option = None; static mut DONE: bool = false; @@ -33,11 +33,15 @@ extern "C" fn handle() { if let Some(message_id) = unsafe { MID.take() } { let delay: u32 = msg::load().unwrap(); - unsafe { DONE = true; } + unsafe { + DONE = true; + } exec::wake_delayed(message_id, delay).expect("Failed to wake message"); } else if unsafe { !DONE } { - unsafe { MID = Some(msg::id()); } + unsafe { + MID = Some(msg::id()); + } exec::wait(); } diff --git a/examples/distributor/Cargo.toml b/examples/distributor/Cargo.toml index 00d2f03613f..73887d6fd87 100644 --- a/examples/distributor/Cargo.toml +++ b/examples/distributor/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-distributor" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -17,8 +18,6 @@ gear-wasm-builder.workspace = true gstd = { workspace = true, features = ["debug"] } gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/distributor/src/lib.rs b/examples/distributor/src/lib.rs index 0508f3a4d2c..53fd344ee84 100644 --- a/examples/distributor/src/lib.rs +++ b/examples/distributor/src/lib.rs @@ -52,174 +52,7 @@ struct Program { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - - use alloc::collections::BTreeSet; - use core::future::Future; - use gstd::{debug, msg, sync::Mutex}; - - static mut STATE: Option = None; - - struct ProgramState { - nodes: Mutex>, - amount: u64, - } - - impl Default for ProgramState { - fn default() -> Self { - Self { - nodes: Mutex::new(BTreeSet::default()), - amount: 0, - } - } - } - - impl Program { - fn new(handle: impl Into) -> Self { - Self { - handle: handle.into(), - } - } - - fn do_request( - &self, - request: Req, - ) -> impl Future> { - let encoded_request: Vec = request.encode(); - - let program_handle = self.handle; - async move { - let reply_bytes = - msg::send_bytes_for_reply(program_handle, &encoded_request[..], 0, 0) - .expect("Error in message sending") - .await - .expect("Error in async message processing"); - - Rep::decode(&mut &reply_bytes[..]).map_err(|_| "Failed to decode reply") - } - } - - async fn do_send(&self, amount: u64) -> Result<(), &'static str> { - match self.do_request(Request::Receive(amount)).await? { - Reply::Success => Ok(()), - _ => Err("Unexpected send reply"), - } - } - - async fn do_report(&self) -> Result { - match self.do_request(Request::Report).await? { - Reply::Amount(amount) => Ok(amount), - _ => Err("Unexpected send reply"), - } - } - - fn nodes() -> &'static Mutex> { - unsafe { &mut STATE.as_mut().expect("STATE UNINITIALIZED!").nodes } - } - - fn amount() -> &'static mut u64 { - unsafe { &mut STATE.as_mut().expect("STATE UNINITIALIZED!").amount } - } - - async fn handle_request() { - let reply = match msg::load::() { - Ok(request) => match request { - Request::Receive(amount) => Self::handle_receive(amount).await, - Request::Join(program_id) => Self::handle_join(program_id).await, - Request::Report => Self::handle_report().await, - }, - Err(e) => { - debug!("Error processing request: {e:?}"); - Reply::Failure - } - }; - - debug!("Handle request finished"); - msg::reply(reply, 0).unwrap(); - } - - async fn handle_receive(amount: u64) -> Reply { - debug!("Handling receive {amount}"); - - let nodes = Program::nodes().lock().await; - let subnodes_count = nodes.as_ref().len() as u64; - - if subnodes_count > 0 { - let distributed_per_node = amount / subnodes_count; - let distributed_total = distributed_per_node * subnodes_count; - let mut left_over = amount - distributed_total; - - if distributed_per_node > 0 { - for program in nodes.as_ref().iter() { - if program.do_send(distributed_per_node).await.is_err() { - // reclaiming amount from nodes that fail! - left_over += distributed_per_node; - } - } - } - - debug!("Set own amount to: {left_over}"); - *Self::amount() = *Self::amount() + left_over; - } else { - debug!("Set own amount to: {amount}"); - *Self::amount() = *Self::amount() + amount; - } - - Reply::Success - } - - async fn handle_join(program_id: ActorId) -> Reply { - let mut nodes = Self::nodes().lock().await; - debug!("Inserting into nodes"); - nodes.as_mut().insert(Program::new(program_id)); - Reply::Success - } - - async fn handle_report() -> Reply { - let mut amount = *Program::amount(); - debug!("Own amount: {amount}"); - - let nodes = Program::nodes().lock().await; - - for program in nodes.as_ref().iter() { - debug!("Querying next node"); - amount += match program.do_report().await { - Ok(amount) => { - debug!("Sub-node result: {amount}"); - amount - } - Err(_) => { - // skipping erroneous sub-nodes! - debug!("Skipping erroneous node"); - 0 - } - } - } - - Reply::Amount(amount) - } - } - - #[no_mangle] - extern "C" fn handle() { - debug!("Handling sequence started"); - gstd::message_loop(Program::handle_request()); - debug!("Handling sequence terminated"); - } - - #[no_mangle] - extern "C" fn handle_reply() { - gstd::record_reply(); - } - - #[no_mangle] - extern "C" fn init() { - unsafe { STATE = Some(Default::default()) }; - msg::reply((), 0).unwrap(); - debug!("Program initialized"); - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/distributor/src/wasm.rs b/examples/distributor/src/wasm.rs new file mode 100644 index 00000000000..0fd4ce4b30d --- /dev/null +++ b/examples/distributor/src/wasm.rs @@ -0,0 +1,182 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{Program, Reply, Request}; +use core::future::Future; +use gstd::{collections::BTreeSet, debug, msg, prelude::*, sync::Mutex, ActorId}; +use parity_scale_codec::{Decode, Encode}; + +static mut STATE: Option = None; + +struct ProgramState { + nodes: Mutex>, + amount: u64, +} + +impl Default for ProgramState { + fn default() -> Self { + Self { + nodes: Mutex::new(BTreeSet::default()), + amount: 0, + } + } +} + +impl Program { + fn new(handle: impl Into) -> Self { + Self { + handle: handle.into(), + } + } + + fn do_request( + &self, + request: Req, + ) -> impl Future> { + let encoded_request: Vec = request.encode(); + + let program_handle = self.handle; + async move { + let reply_bytes = msg::send_bytes_for_reply(program_handle, &encoded_request[..], 0, 0) + .expect("Error in message sending") + .await + .expect("Error in async message processing"); + + Rep::decode(&mut &reply_bytes[..]).map_err(|_| "Failed to decode reply") + } + } + + async fn do_send(&self, amount: u64) -> Result<(), &'static str> { + match self.do_request(Request::Receive(amount)).await? { + Reply::Success => Ok(()), + _ => Err("Unexpected send reply"), + } + } + + async fn do_report(&self) -> Result { + match self.do_request(Request::Report).await? { + Reply::Amount(amount) => Ok(amount), + _ => Err("Unexpected send reply"), + } + } + + fn nodes() -> &'static Mutex> { + unsafe { &mut STATE.as_mut().expect("STATE UNINITIALIZED!").nodes } + } + + fn amount() -> &'static mut u64 { + unsafe { &mut STATE.as_mut().expect("STATE UNINITIALIZED!").amount } + } + + async fn handle_request() { + let reply = match msg::load::() { + Ok(request) => match request { + Request::Receive(amount) => Self::handle_receive(amount).await, + Request::Join(program_id) => Self::handle_join(program_id).await, + Request::Report => Self::handle_report().await, + }, + Err(e) => { + debug!("Error processing request: {e:?}"); + Reply::Failure + } + }; + + debug!("Handle request finished"); + msg::reply(reply, 0).unwrap(); + } + + async fn handle_receive(amount: u64) -> Reply { + debug!("Handling receive {amount}"); + + let nodes = Program::nodes().lock().await; + let subnodes_count = nodes.as_ref().len() as u64; + + if subnodes_count > 0 { + let distributed_per_node = amount / subnodes_count; + let distributed_total = distributed_per_node * subnodes_count; + let mut left_over = amount - distributed_total; + + if distributed_per_node > 0 { + for program in nodes.as_ref().iter() { + if program.do_send(distributed_per_node).await.is_err() { + // reclaiming amount from nodes that fail! + left_over += distributed_per_node; + } + } + } + + debug!("Set own amount to: {left_over}"); + *Self::amount() = *Self::amount() + left_over; + } else { + debug!("Set own amount to: {amount}"); + *Self::amount() = *Self::amount() + amount; + } + + Reply::Success + } + + async fn handle_join(program_id: ActorId) -> Reply { + let mut nodes = Self::nodes().lock().await; + debug!("Inserting into nodes"); + nodes.as_mut().insert(Program::new(program_id)); + Reply::Success + } + + async fn handle_report() -> Reply { + let mut amount = *Program::amount(); + debug!("Own amount: {amount}"); + + let nodes = Program::nodes().lock().await; + + for program in nodes.as_ref().iter() { + debug!("Querying next node"); + amount += match program.do_report().await { + Ok(amount) => { + debug!("Sub-node result: {amount}"); + amount + } + Err(_) => { + // skipping erroneous sub-nodes! + debug!("Skipping erroneous node"); + 0 + } + } + } + + Reply::Amount(amount) + } +} + +#[no_mangle] +extern "C" fn handle() { + debug!("Handling sequence started"); + gstd::message_loop(Program::handle_request()); + debug!("Handling sequence terminated"); +} + +#[no_mangle] +extern "C" fn handle_reply() { + gstd::record_reply(); +} + +#[no_mangle] +extern "C" fn init() { + unsafe { STATE = Some(Default::default()) }; + msg::reply((), 0).unwrap(); + debug!("Program initialized"); +} diff --git a/examples/fungible-token/Cargo.toml b/examples/fungible-token/Cargo.toml index 11d3a446136..18d977de144 100644 --- a/examples/fungible-token/Cargo.toml +++ b/examples/fungible-token/Cargo.toml @@ -4,25 +4,26 @@ version = "0.1.4" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] -gstd = { workspace = true } -hashbrown = { workspace = true } -gmeta = { workspace = true } +gstd.workspace = true +hashbrown.workspace = true +gmeta.workspace = true ft-io = { path = "io" } [dev-dependencies] gstd = { workspace = true, features = ["debug"] } gear-core.workspace = true -tokio = { workspace = true, features = ["full", "test-util"] } -gclient = { workspace = true } -futures = "0.3" -rand = { version = "0.8" } +tokio.workspace = true +gclient.workspace = true +futures.workspace = true +rand.workspace = true statrs = "0.16" [build-dependencies] -gear-wasm-builder = { workspace = true } +gear-wasm-builder.workspace = true [features] debug = ["gstd/debug"] diff --git a/examples/fungible-token/io/Cargo.toml b/examples/fungible-token/io/Cargo.toml index 2f7fefeaa90..7fd0537bf5c 100644 --- a/examples/fungible-token/io/Cargo.toml +++ b/examples/fungible-token/io/Cargo.toml @@ -4,8 +4,9 @@ version = "0.1.4" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../../" +homepage.workspace = true +repository.workspace = true [dependencies] -gstd = { workspace = true } -gmeta = { workspace = true } +gstd.workspace = true +gmeta.workspace = true diff --git a/examples/fungible-token/src/lib.rs b/examples/fungible-token/src/lib.rs index f135d5bfcd3..3c6944afdde 100644 --- a/examples/fungible-token/src/lib.rs +++ b/examples/fungible-token/src/lib.rs @@ -30,4 +30,4 @@ pub use code::WASM_BINARY_OPT as WASM_BINARY; pub const WASM_BINARY: &[u8] = &[]; #[cfg(not(feature = "std"))] -pub mod contract; +pub mod wasm; diff --git a/examples/fungible-token/src/contract.rs b/examples/fungible-token/src/wasm.rs similarity index 100% rename from examples/fungible-token/src/contract.rs rename to examples/fungible-token/src/wasm.rs diff --git a/examples/futures-unordered/Cargo.toml b/examples/futures-unordered/Cargo.toml index df6d483f4a6..b20a78af317 100644 --- a/examples/futures-unordered/Cargo.toml +++ b/examples/futures-unordered/Cargo.toml @@ -4,12 +4,13 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } -futures = { workspace = true, features = ["alloc", "async-await"] } +parity-scale-codec.workspace = true +futures = { workspace = true, features = ["async-await"] } [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/futures-unordered/src/lib.rs b/examples/futures-unordered/src/lib.rs index 0144a88f59a..aafc99aa7fd 100644 --- a/examples/futures-unordered/src/lib.rs +++ b/examples/futures-unordered/src/lib.rs @@ -36,113 +36,4 @@ pub enum Command { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::Command; - use futures::{ - join, select_biased, - stream::{FuturesUnordered, StreamExt}, - }; - use gstd::{debug, msg, prelude::*, ActorId}; - - static mut DEMO_ASYNC: ActorId = ActorId::new([0u8; 32]); - static mut DEMO_PING: ActorId = ActorId::new([0u8; 32]); - - #[no_mangle] - extern "C" fn init() { - let (demo_async, demo_ping) = msg::load().expect("Failed to load destination"); - unsafe { - DEMO_ASYNC = demo_async; - DEMO_PING = demo_ping; - } - } - - enum Dest { - Async, - Ping, - } - - #[gstd::async_main] - async fn main() { - let command = msg::load().expect("Failed to load command"); - let source = msg::source(); - - let send_fut = |dest: Dest| { - let (destination, payload) = match dest { - Dest::Async => (unsafe { DEMO_ASYNC }, vec![0]), // demo_async::Command::Common - Dest::Ping => (unsafe { DEMO_PING }, b"PING".to_vec()), - }; - - msg::send_bytes_for_reply(destination, payload, 0, 0).expect("Failed to send message") - }; - - match command { - // Directly using stream from futures unordered - // to step through each future ready - Command::Unordered => { - debug!("UNORDERED: Before any sending"); - - let requests = vec![send_fut(Dest::Async), send_fut(Dest::Ping)]; - let mut unordered: FuturesUnordered<_> = requests.into_iter().collect(); - - debug!("Before any polls"); - - let first = unordered.next().await; - msg::send_bytes( - source, - first.expect("Infallible").expect("Received error reply"), - 0, - ) - .expect("Failed to send message"); - - debug!("First (from demo_ping) done"); - - let second = unordered.next().await; - msg::send_bytes( - source, - second.expect("Infallible").expect("Received error reply"), - 0, - ) - .expect("Failed to send message"); - - debug!("Second (from demo_async) done"); - } - // Using select! macro to wait for first future ready - Command::Select => { - debug!("SELECT: Before any sending"); - - select_biased! { - res = send_fut(Dest::Async) => { - debug!("Received msg from demo_async"); - msg::send_bytes(source, res.expect("Received error reply"), 0).expect("Failed to send message"); - }, - res = send_fut(Dest::Ping) => { - debug!("Received msg from demo_ping"); - msg::send_bytes(source, res.expect("Received error reply"), 0).expect("Failed to send message"); - }, - }; - - debug!("Finish after select"); - } - // Using join! macros to wait all features ready - Command::Join => { - debug!("JOIN: Before any sending"); - - let res = join!(send_fut(Dest::Async), send_fut(Dest::Ping)); - - debug!("Finish after join"); - - let mut r1 = res.0.expect("Received error reply"); - let mut r2 = res.1.expect("Received error reply"); - - let mut res = Vec::with_capacity(r1.len() + r2.len()); - - res.append(&mut r1); - res.append(&mut r2); - - msg::send_bytes(source, res, 0).expect("Failed to send message"); - } - } - - msg::reply_bytes(msg::id(), 0).expect("Failed to send reply"); - } -} +mod wasm; diff --git a/examples/futures-unordered/src/wasm.rs b/examples/futures-unordered/src/wasm.rs new file mode 100644 index 00000000000..dcc301caaf2 --- /dev/null +++ b/examples/futures-unordered/src/wasm.rs @@ -0,0 +1,126 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Command; +use futures::{ + join, select_biased, + stream::{FuturesUnordered, StreamExt}, +}; +use gstd::{debug, msg, prelude::*, ActorId}; + +static mut DEMO_ASYNC: ActorId = ActorId::new([0u8; 32]); +static mut DEMO_PING: ActorId = ActorId::new([0u8; 32]); + +#[no_mangle] +extern "C" fn init() { + let (demo_async, demo_ping) = msg::load().expect("Failed to load destination"); + unsafe { + DEMO_ASYNC = demo_async; + DEMO_PING = demo_ping; + } +} + +enum Dest { + Async, + Ping, +} + +#[gstd::async_main] +async fn main() { + let command = msg::load().expect("Failed to load command"); + let source = msg::source(); + + let send_fut = |dest: Dest| { + let (destination, payload) = match dest { + Dest::Async => (unsafe { DEMO_ASYNC }, vec![0]), // demo_async::Command::Common + Dest::Ping => (unsafe { DEMO_PING }, b"PING".to_vec()), + }; + + msg::send_bytes_for_reply(destination, payload, 0, 0).expect("Failed to send message") + }; + + match command { + // Directly using stream from futures unordered + // to step through each future ready + Command::Unordered => { + debug!("UNORDERED: Before any sending"); + + let requests = vec![send_fut(Dest::Async), send_fut(Dest::Ping)]; + let mut unordered: FuturesUnordered<_> = requests.into_iter().collect(); + + debug!("Before any polls"); + + let first = unordered.next().await; + msg::send_bytes( + source, + first.expect("Infallible").expect("Received error reply"), + 0, + ) + .expect("Failed to send message"); + + debug!("First (from demo_ping) done"); + + let second = unordered.next().await; + msg::send_bytes( + source, + second.expect("Infallible").expect("Received error reply"), + 0, + ) + .expect("Failed to send message"); + + debug!("Second (from demo_async) done"); + } + // Using select! macro to wait for first future ready + Command::Select => { + debug!("SELECT: Before any sending"); + + select_biased! { + res = send_fut(Dest::Async) => { + debug!("Received msg from demo_async"); + msg::send_bytes(source, res.expect("Received error reply"), 0).expect("Failed to send message"); + }, + res = send_fut(Dest::Ping) => { + debug!("Received msg from demo_ping"); + msg::send_bytes(source, res.expect("Received error reply"), 0).expect("Failed to send message"); + }, + }; + + debug!("Finish after select"); + } + // Using join! macros to wait all features ready + Command::Join => { + debug!("JOIN: Before any sending"); + + let res = join!(send_fut(Dest::Async), send_fut(Dest::Ping)); + + debug!("Finish after join"); + + let mut r1 = res.0.expect("Received error reply"); + let mut r2 = res.1.expect("Received error reply"); + + let mut res = Vec::with_capacity(r1.len() + r2.len()); + + res.append(&mut r1); + res.append(&mut r2); + + msg::send_bytes(source, res, 0).expect("Failed to send message"); + } + } + + msg::reply_bytes(msg::id(), 0).expect("Failed to send reply"); +} diff --git a/examples/gas-burned/Cargo.toml b/examples/gas-burned/Cargo.toml index 656a5b96c4c..9ad1445febf 100644 --- a/examples/gas-burned/Cargo.toml +++ b/examples/gas-burned/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-gas-burned" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/gas-burned/src/lib.rs b/examples/gas-burned/src/lib.rs index 85514ae9dbb..45e628fea74 100644 --- a/examples/gas-burned/src/lib.rs +++ b/examples/gas-burned/src/lib.rs @@ -9,29 +9,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, prelude::*}; - - const SHORT: usize = 100; - const LONG: usize = 10000; - - #[no_mangle] - extern "C" fn init() { - let mut v = vec![0; SHORT]; - for (i, item) in v.iter_mut().enumerate() { - *item = i * i; - } - msg::reply_bytes(format!("init: {}", v.into_iter().sum::()), 0).unwrap(); - } - - #[no_mangle] - extern "C" fn handle() { - let mut v = vec![0; LONG]; - for (i, item) in v.iter_mut().enumerate() { - *item = i * i; - } - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/gas-burned/src/wasm.rs b/examples/gas-burned/src/wasm.rs new file mode 100644 index 00000000000..9e4fcf19f35 --- /dev/null +++ b/examples/gas-burned/src/wasm.rs @@ -0,0 +1,21 @@ +use gstd::{msg, prelude::*}; + +const SHORT: usize = 100; +const LONG: usize = 10000; + +#[no_mangle] +extern "C" fn init() { + let mut v = vec![0; SHORT]; + for (i, item) in v.iter_mut().enumerate() { + *item = i * i; + } + msg::reply_bytes(format!("init: {}", v.into_iter().sum::()), 0).unwrap(); +} + +#[no_mangle] +extern "C" fn handle() { + let mut v = vec![0; LONG]; + for (i, item) in v.iter_mut().enumerate() { + *item = i * i; + } +} diff --git a/examples/incomplete-async-payloads/Cargo.toml b/examples/incomplete-async-payloads/Cargo.toml index 10dd073012e..1c4ef56a519 100644 --- a/examples/incomplete-async-payloads/Cargo.toml +++ b/examples/incomplete-async-payloads/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/incomplete-async-payloads/src/lib.rs b/examples/incomplete-async-payloads/src/lib.rs index eb8cc63eb20..4e40051df18 100644 --- a/examples/incomplete-async-payloads/src/lib.rs +++ b/examples/incomplete-async-payloads/src/lib.rs @@ -37,60 +37,4 @@ pub enum Command { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::Command; - use gstd::{ - msg::{self, MessageHandle}, - prelude::*, - ActorId, - }; - - static mut DESTINATION: ActorId = ActorId::zero(); - - #[no_mangle] - extern "C" fn init() { - let destination = msg::load().expect("Failed to load destination"); - unsafe { DESTINATION = destination }; - } - - async fn ping() { - msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) - .expect("Failed to send message") - .await - .expect("Received error reply"); - } - - #[gstd::async_main] - async fn main() { - let command = msg::load().expect("Failed to load command"); - - match command { - Command::HandleStore => { - let handle = MessageHandle::init().expect("Failed to init message"); - handle.push(b"STORED ").expect("Failed to push payload"); - ping().await; - handle.push("COMMON").expect("Failed to push payload"); - handle - .commit(msg::source(), 0) - .expect("Failed to commit message"); - } - Command::ReplyStore => { - msg::reply_push(b"STORED ").expect("Failed to push reply payload"); - ping().await; - msg::reply_push(b"REPLY").expect("Failed to push reply payload"); - msg::reply_commit(0).expect("Failed to commit reply"); - } - Command::Handle => { - let handle = MessageHandle::init().expect("Failed to init message"); - handle.push(b"OK PING").expect("Failed to push payload"); - handle - .commit(msg::source(), 0) - .expect("Failed to commit message"); - } - Command::Reply => { - msg::reply_push(b"OK REPLY").expect("Failed to push reply payload"); - msg::reply_commit(0).expect("Failed to commit reply"); - } - } - } -} +mod wasm; diff --git a/examples/incomplete-async-payloads/src/wasm.rs b/examples/incomplete-async-payloads/src/wasm.rs new file mode 100644 index 00000000000..987f22ff05d --- /dev/null +++ b/examples/incomplete-async-payloads/src/wasm.rs @@ -0,0 +1,73 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Command; +use gstd::{ + msg::{self, MessageHandle}, + prelude::*, + ActorId, +}; + +static mut DESTINATION: ActorId = ActorId::zero(); + +#[no_mangle] +extern "C" fn init() { + let destination = msg::load().expect("Failed to load destination"); + unsafe { DESTINATION = destination }; +} + +async fn ping() { + msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) + .expect("Failed to send message") + .await + .expect("Received error reply"); +} + +#[gstd::async_main] +async fn main() { + let command = msg::load().expect("Failed to load command"); + + match command { + Command::HandleStore => { + let handle = MessageHandle::init().expect("Failed to init message"); + handle.push(b"STORED ").expect("Failed to push payload"); + ping().await; + handle.push("COMMON").expect("Failed to push payload"); + handle + .commit(msg::source(), 0) + .expect("Failed to commit message"); + } + Command::ReplyStore => { + msg::reply_push(b"STORED ").expect("Failed to push reply payload"); + ping().await; + msg::reply_push(b"REPLY").expect("Failed to push reply payload"); + msg::reply_commit(0).expect("Failed to commit reply"); + } + Command::Handle => { + let handle = MessageHandle::init().expect("Failed to init message"); + handle.push(b"OK PING").expect("Failed to push payload"); + handle + .commit(msg::source(), 0) + .expect("Failed to commit message"); + } + Command::Reply => { + msg::reply_push(b"OK REPLY").expect("Failed to push reply payload"); + msg::reply_commit(0).expect("Failed to commit reply"); + } + } +} diff --git a/examples/init-fail-sender/Cargo.toml b/examples/init-fail-sender/Cargo.toml index 7406dd2fd2d..34611c8f1fb 100644 --- a/examples/init-fail-sender/Cargo.toml +++ b/examples/init-fail-sender/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-init-fail-sender" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true @@ -12,8 +13,6 @@ gstd.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/init-fail-sender/src/lib.rs b/examples/init-fail-sender/src/lib.rs index 81a7e8ec5a3..a9c048cd09d 100644 --- a/examples/init-fail-sender/src/lib.rs +++ b/examples/init-fail-sender/src/lib.rs @@ -17,20 +17,4 @@ pub fn reply_duration() -> u32 { } #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, ActorId}; - - #[gstd::async_init] - async fn init() { - let value_receiver: ActorId = msg::load().unwrap(); - - msg::send_bytes_with_gas(value_receiver, [], 50_000, 1_000).unwrap(); - msg::send_bytes_with_gas_for_reply(msg::source(), [], 30_000, 0, 0) - .unwrap() - .exactly(Some(super::reply_duration())) - .unwrap() - .await - .expect("Failed to send message"); - panic!(); - } -} +mod wasm; diff --git a/examples/init-fail-sender/src/wasm.rs b/examples/init-fail-sender/src/wasm.rs new file mode 100644 index 00000000000..4bca976c3bb --- /dev/null +++ b/examples/init-fail-sender/src/wasm.rs @@ -0,0 +1,15 @@ +use gstd::{msg, ActorId}; + +#[gstd::async_init] +async fn init() { + let value_receiver: ActorId = msg::load().unwrap(); + + msg::send_bytes_with_gas(value_receiver, [], 50_000, 1_000).unwrap(); + msg::send_bytes_with_gas_for_reply(msg::source(), [], 30_000, 0, 0) + .unwrap() + .exactly(Some(super::reply_duration())) + .unwrap() + .await + .expect("Failed to send message"); + panic!(); +} diff --git a/examples/init-wait-reply-exit/Cargo.toml b/examples/init-wait-reply-exit/Cargo.toml index 52f98b3149b..71b6321fe47 100644 --- a/examples/init-wait-reply-exit/Cargo.toml +++ b/examples/init-wait-reply-exit/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-init-wait-reply-exit" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true @@ -12,8 +13,6 @@ gstd.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/init-wait-reply-exit/src/lib.rs b/examples/init-wait-reply-exit/src/lib.rs index e5e4ef17484..813a4f3ed30 100644 --- a/examples/init-wait-reply-exit/src/lib.rs +++ b/examples/init-wait-reply-exit/src/lib.rs @@ -9,6 +9,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; diff --git a/examples/init-wait-reply-exit/src/code.rs b/examples/init-wait-reply-exit/src/wasm.rs similarity index 100% rename from examples/init-wait-reply-exit/src/code.rs rename to examples/init-wait-reply-exit/src/wasm.rs diff --git a/examples/init-wait/Cargo.toml b/examples/init-wait/Cargo.toml index 5a7c643798f..de95cd060ee 100644 --- a/examples/init-wait/Cargo.toml +++ b/examples/init-wait/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/init-wait/src/lib.rs b/examples/init-wait/src/lib.rs index a4fc7ea8b42..6099c100244 100644 --- a/examples/init-wait/src/lib.rs +++ b/examples/init-wait/src/lib.rs @@ -27,6 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; diff --git a/examples/init-wait/src/code.rs b/examples/init-wait/src/wasm.rs similarity index 97% rename from examples/init-wait/src/code.rs rename to examples/init-wait/src/wasm.rs index c6ef0bc9edc..7d3ecdc3f0b 100644 --- a/examples/init-wait/src/code.rs +++ b/examples/init-wait/src/wasm.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use gstd::{exec, msg, collections::BTreeMap, MessageId}; +use gstd::{collections::BTreeMap, exec, msg, MessageId}; #[derive(PartialEq, Debug)] enum State { diff --git a/examples/messager/Cargo.toml b/examples/messager/Cargo.toml index cc570e340ab..884b045bec8 100644 --- a/examples/messager/Cargo.toml +++ b/examples/messager/Cargo.toml @@ -2,9 +2,8 @@ name = "demo-messager" version = "0.1.0" authors.workspace = true -license = "GPL-3.0" +license.workspace = true edition.workspace = true -workspace = "../../" [dependencies] gstd.workspace = true diff --git a/examples/messager/src/lib.rs b/examples/messager/src/lib.rs index 3616ca8a492..fd16790cf82 100644 --- a/examples/messager/src/lib.rs +++ b/examples/messager/src/lib.rs @@ -27,9 +27,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; pub const SEND_REPLY: &[u8] = b"send"; pub const REPLY_REPLY: &[u8] = b"reply"; diff --git a/examples/messager/src/code.rs b/examples/messager/src/wasm.rs similarity index 100% rename from examples/messager/src/code.rs rename to examples/messager/src/wasm.rs diff --git a/examples/mul-by-const/Cargo.toml b/examples/mul-by-const/Cargo.toml index adbd0248877..64e2f97f889 100644 --- a/examples/mul-by-const/Cargo.toml +++ b/examples/mul-by-const/Cargo.toml @@ -2,19 +2,18 @@ name = "demo-mul-by-const" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/mul-by-const/src/lib.rs b/examples/mul-by-const/src/lib.rs index 0076d711649..cc2e84c6fc0 100644 --- a/examples/mul-by-const/src/lib.rs +++ b/examples/mul-by-const/src/lib.rs @@ -34,60 +34,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - extern crate alloc; - - use gstd::{debug, exec, msg, String}; - - static mut DEBUG: DebugInfo = DebugInfo { me: String::new() }; - static mut STATE: State = State { intrinsic: 0 }; - - struct DebugInfo { - me: String, - } - - struct State { - intrinsic: u64, - } - - impl State { - fn new(value: u64) -> Self { - Self { intrinsic: value } - } - - unsafe fn unchecked_mul(&mut self, other: u64) -> u64 { - let z: u64 = self - .intrinsic - .checked_mul(other) - .expect("Multiplication overflow"); - debug!( - "[0x{} mul_by_const::unchecked_mul] Calculated {} x {other} == {z}", - DEBUG.me, self.intrinsic - ); - z - } - } - - #[no_mangle] - extern "C" fn handle() { - let x: u64 = msg::load().expect("Expecting a u64 number"); - - msg::reply(unsafe { STATE.unchecked_mul(x) }, 0).unwrap(); - } - - #[no_mangle] - extern "C" fn init() { - let val: u64 = msg::load().expect("Expecting a u64 number"); - unsafe { - STATE = State::new(val); - DEBUG = DebugInfo { - me: hex::encode(exec::program_id()), - }; - } - msg::reply_bytes([], 0).unwrap(); - debug!( - "[0x{} mul_by_const::init] Program initialized with input {val}", - unsafe { &DEBUG.me }, - ); - } -} +mod wasm; diff --git a/examples/mul-by-const/src/wasm.rs b/examples/mul-by-const/src/wasm.rs new file mode 100644 index 00000000000..15dfeb404c8 --- /dev/null +++ b/examples/mul-by-const/src/wasm.rs @@ -0,0 +1,80 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// This contract recursively composes itself with another contract (the other contract +// being applied to the input data first): `c(f) = (c(f) . f) x`. +// Every call to the auto_composer contract increments the internal `ITER` counter. +// As soon as the counter reaches the `MAX_ITER`, the recursion stops. +// Effectively, this procedure executes a composition of `MAX_ITER` contracts `f` +// where the output of the previous call is fed to the input of the next call. + +extern crate alloc; + +use gstd::{debug, exec, msg, String}; + +static mut DEBUG: DebugInfo = DebugInfo { me: String::new() }; +static mut STATE: State = State { intrinsic: 0 }; + +struct DebugInfo { + me: String, +} + +struct State { + intrinsic: u64, +} + +impl State { + fn new(value: u64) -> Self { + Self { intrinsic: value } + } + + unsafe fn unchecked_mul(&mut self, other: u64) -> u64 { + let z: u64 = self + .intrinsic + .checked_mul(other) + .expect("Multiplication overflow"); + debug!( + "[0x{} mul_by_const::unchecked_mul] Calculated {} x {other} == {z}", + DEBUG.me, self.intrinsic + ); + z + } +} + +#[no_mangle] +extern "C" fn handle() { + let x: u64 = msg::load().expect("Expecting a u64 number"); + + msg::reply(unsafe { STATE.unchecked_mul(x) }, 0).unwrap(); +} + +#[no_mangle] +extern "C" fn init() { + let val: u64 = msg::load().expect("Expecting a u64 number"); + unsafe { + STATE = State::new(val); + DEBUG = DebugInfo { + me: hex::encode(exec::program_id()), + }; + } + msg::reply_bytes([], 0).unwrap(); + debug!( + "[0x{} mul_by_const::init] Program initialized with input {val}", + unsafe { &DEBUG.me }, + ); +} diff --git a/examples/ncompose/Cargo.toml b/examples/ncompose/Cargo.toml index c5ae98c9182..9cb597e461a 100644 --- a/examples/ncompose/Cargo.toml +++ b/examples/ncompose/Cargo.toml @@ -2,19 +2,18 @@ name = "demo-ncompose" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/ncompose/src/lib.rs b/examples/ncompose/src/lib.rs index 5aa2d603784..cc2e84c6fc0 100644 --- a/examples/ncompose/src/lib.rs +++ b/examples/ncompose/src/lib.rs @@ -34,134 +34,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - extern crate alloc; - - use gstd::{debug, exec, msg, prelude::*, ActorId}; - - static mut STATE: State = State { - iter: 0, - max_iter: 0, - me: Program { - handle: ActorId::new([0u8; 32]), - }, - other: Program { - handle: ActorId::new([0u8; 32]), - }, - }; - - struct State { - iter: u16, - max_iter: u16, - me: Program, - other: Program, - } - - impl State { - fn new(max_iter: u16, actor: impl Into) -> Self { - Self { - iter: 0, - max_iter, - me: Program::new(exec::program_id()), - other: Program::new(actor), - } - } - - fn inc(&mut self) { - self.iter += 1; - } - - async fn compose_with_self(&mut self, input: Vec) -> Result, &'static str> { - if self.iter >= self.max_iter { - debug!( - "[0x{} ncompose::compose_with_self] Max number of iterations {} reached; no further progress is possible", - hex::encode(self.me.handle), - self.max_iter - ); - return Err("Max iteration reached"); - } - // Increase iter - self.inc(); - - debug!( - "[ncompose::compose_with_self] Iter: {} out of {}", - self.iter, self.max_iter - ); - - // Pass the input to the `other` contract first - debug!( - "[0x{} ncompose::compose_with_self] Calling contract 0x{}", - hex::encode(self.me.handle), - hex::encode(self.other.handle) - ); - let output_other = self.other.call(input).await?; - debug!( - "[0x{} ncompose::compose_with_self] Calling self", - hex::encode(exec::program_id()) - ); - let output = self.me.call(output_other).await?; - debug!( - "[0x{} ncompose::compose_with_self] Composition output: {output:?}", - hex::encode(exec::program_id()), - ); - - Ok(output) - } - } - - #[derive(Eq, Ord, PartialEq, PartialOrd)] - struct Program { - handle: ActorId, - } - - impl Program { - fn new(handle: impl Into) -> Self { - Self { - handle: handle.into(), - } - } - - async fn call(&self, input: Vec) -> Result, &'static str> { - let reply_bytes = msg::send_bytes_for_reply(self.handle, &input[..], 0, 0) - .expect("Error sending message") - .await - .map_err(|_| "Error in async message processing")?; - debug!( - "[0x{} ncompose::Program::call] Received reply from remote contract: {:?}", - hex::encode(exec::program_id()), - hex::encode(&reply_bytes) - ); - - Ok(reply_bytes) - } - } - - #[gstd::async_main] - async fn main() { - let input = msg::load_bytes().expect("Failed to load payload bytes"); - debug!( - "[0x{} ncompose::handle] input = {input:?}", - hex::encode(unsafe { STATE.me.handle }), - ); - - if let Ok(outcome) = (unsafe { STATE.compose_with_self(input) }).await { - debug!( - "[0x{} ncompose::handle] Composition output: {outcome:?}", - hex::encode(exec::program_id()), - ); - msg::reply(outcome, 0).unwrap(); - } - } - - #[no_mangle] - extern "C" fn init() { - let (actor, max_iter): (ActorId, u16) = - msg::load().expect("Malformed input: expecting a program ID and a number"); - unsafe { STATE = State::new(max_iter, actor) }; - msg::reply_bytes([], 0).unwrap(); - debug!( - "[0x{} ncompose::init] Program initialized", - hex::encode(exec::program_id()) - ); - } -} +mod wasm; diff --git a/examples/ncompose/src/wasm.rs b/examples/ncompose/src/wasm.rs new file mode 100644 index 00000000000..1b4c1276c5e --- /dev/null +++ b/examples/ncompose/src/wasm.rs @@ -0,0 +1,154 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// This contract recursively composes itself with another contract (the other contract +// being applied to the input data first): `c(f) = (c(f) . f) x`. +// Every call to the auto_composer contract increments the internal `ITER` counter. +// As soon as the counter reaches the `MAX_ITER`, the recursion stops. +// Effectively, this procedure executes a composition of `MAX_ITER` contracts `f` +// where the output of the previous call is fed to the input of the next call. + +extern crate alloc; + +use gstd::{debug, exec, msg, prelude::*, ActorId}; + +static mut STATE: State = State { + iter: 0, + max_iter: 0, + me: Program { + handle: ActorId::new([0u8; 32]), + }, + other: Program { + handle: ActorId::new([0u8; 32]), + }, +}; + +struct State { + iter: u16, + max_iter: u16, + me: Program, + other: Program, +} + +impl State { + fn new(max_iter: u16, actor: impl Into) -> Self { + Self { + iter: 0, + max_iter, + me: Program::new(exec::program_id()), + other: Program::new(actor), + } + } + + fn inc(&mut self) { + self.iter += 1; + } + + async fn compose_with_self(&mut self, input: Vec) -> Result, &'static str> { + if self.iter >= self.max_iter { + debug!( + "[0x{} ncompose::compose_with_self] Max number of iterations {} reached; no further progress is possible", + hex::encode(self.me.handle), + self.max_iter + ); + return Err("Max iteration reached"); + } + // Increase iter + self.inc(); + + debug!( + "[ncompose::compose_with_self] Iter: {} out of {}", + self.iter, self.max_iter + ); + + // Pass the input to the `other` contract first + debug!( + "[0x{} ncompose::compose_with_self] Calling contract 0x{}", + hex::encode(self.me.handle), + hex::encode(self.other.handle) + ); + let output_other = self.other.call(input).await?; + debug!( + "[0x{} ncompose::compose_with_self] Calling self", + hex::encode(exec::program_id()) + ); + let output = self.me.call(output_other).await?; + debug!( + "[0x{} ncompose::compose_with_self] Composition output: {output:?}", + hex::encode(exec::program_id()), + ); + + Ok(output) + } +} + +#[derive(Eq, Ord, PartialEq, PartialOrd)] +struct Program { + handle: ActorId, +} + +impl Program { + fn new(handle: impl Into) -> Self { + Self { + handle: handle.into(), + } + } + + async fn call(&self, input: Vec) -> Result, &'static str> { + let reply_bytes = msg::send_bytes_for_reply(self.handle, &input[..], 0, 0) + .expect("Error sending message") + .await + .map_err(|_| "Error in async message processing")?; + debug!( + "[0x{} ncompose::Program::call] Received reply from remote contract: {:?}", + hex::encode(exec::program_id()), + hex::encode(&reply_bytes) + ); + + Ok(reply_bytes) + } +} + +#[gstd::async_main] +async fn main() { + let input = msg::load_bytes().expect("Failed to load payload bytes"); + debug!( + "[0x{} ncompose::handle] input = {input:?}", + hex::encode(unsafe { STATE.me.handle }), + ); + + if let Ok(outcome) = (unsafe { STATE.compose_with_self(input) }).await { + debug!( + "[0x{} ncompose::handle] Composition output: {outcome:?}", + hex::encode(exec::program_id()), + ); + msg::reply(outcome, 0).unwrap(); + } +} + +#[no_mangle] +extern "C" fn init() { + let (actor, max_iter): (ActorId, u16) = + msg::load().expect("Malformed input: expecting a program ID and a number"); + unsafe { STATE = State::new(max_iter, actor) }; + msg::reply_bytes([], 0).unwrap(); + debug!( + "[0x{} ncompose::init] Program initialized", + hex::encode(exec::program_id()) + ); +} diff --git a/examples/new-meta/Cargo.toml b/examples/new-meta/Cargo.toml index fb1bb189f7c..f78115c3251 100644 --- a/examples/new-meta/Cargo.toml +++ b/examples/new-meta/Cargo.toml @@ -2,17 +2,18 @@ name = "demo-new-meta" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] +gstd.workspace = true parity-scale-codec.workspace = true demo-meta-io = { path = "io" } demo-meta-state-v1 = { path = "state-v1", default-features = false, optional = true } demo-meta-state-v2 = { path = "state-v2", default-features = false, optional = true } demo-meta-state-v3 = { path = "state-v3", default-features = false, optional = true } -gstd.workspace = true [build-dependencies] demo-meta-io = { path = "io" } diff --git a/examples/new-meta/io/Cargo.toml b/examples/new-meta/io/Cargo.toml index 071bf9c8993..6bd4fedeaed 100644 --- a/examples/new-meta/io/Cargo.toml +++ b/examples/new-meta/io/Cargo.toml @@ -4,9 +4,10 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../../" +homepage.workspace = true +repository.workspace = true [dependencies] -gmeta = { workspace = true } -scale-info = { workspace = true, features = ["derive"] } -parity-scale-codec = { workspace = true, features = ["derive"] } +gmeta.workspace = true +scale-info.workspace = true +parity-scale-codec.workspace = true diff --git a/examples/new-meta/state-v1/Cargo.toml b/examples/new-meta/state-v1/Cargo.toml index 901bd45dde5..df273f240cf 100644 --- a/examples/new-meta/state-v1/Cargo.toml +++ b/examples/new-meta/state-v1/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/new-meta/state-v2/Cargo.toml b/examples/new-meta/state-v2/Cargo.toml index b5c0becefb6..b63c6aec983 100644 --- a/examples/new-meta/state-v2/Cargo.toml +++ b/examples/new-meta/state-v2/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/new-meta/state-v3/Cargo.toml b/examples/new-meta/state-v3/Cargo.toml index 379155bff3f..7724c477251 100644 --- a/examples/new-meta/state-v3/Cargo.toml +++ b/examples/new-meta/state-v3/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/new-meta/tests/read_state.rs b/examples/new-meta/tests/read_state.rs index 7ee30cc09bb..9c7fffdc380 100644 --- a/examples/new-meta/tests/read_state.rs +++ b/examples/new-meta/tests/read_state.rs @@ -10,7 +10,7 @@ fn read_state_bytes_returns_full_state() { let program = initialize_current_program(&system); let actual_state = program - .read_state_bytes() + .read_state_bytes(Default::default()) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().encode(); @@ -26,7 +26,12 @@ fn read_state_bytes_with_wasm_func_returns_transformed_state() { assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); let actual_state = program - .read_state_bytes_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args_encoded!()) + .read_state_bytes_using_wasm( + Default::default(), + FUNC_NAME, + META_WASM_V1.to_vec(), + state_args_encoded!(), + ) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().first().encode(); @@ -47,6 +52,7 @@ fn read_state_bytes_with_parameterized_wasm_func_returns_transformed_state() { let actual_state = program .read_state_bytes_using_wasm( + Default::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args_encoded!(&other_person), @@ -73,6 +79,7 @@ fn read_state_bytes_with_two_args_wasm_func_returns_transformed_state() { let actual_state = program .read_state_bytes_using_wasm( + Default::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args_encoded!(name.clone(), surname.clone()), @@ -92,7 +99,9 @@ fn read_state_returns_full_state() { let system = System::new(); let program = initialize_current_program(&system); - let actual_state: Vec = program.read_state().expect("Unable to read program state"); + let actual_state: Vec = program + .read_state(Vec::::default()) + .expect("Unable to read program state"); let expected_state = Wallet::test_sequence(); @@ -107,7 +116,12 @@ fn read_state_with_wasm_func_returns_transformed_state() { assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); let actual_state: Option = program - .read_state_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args!()) + .read_state_using_wasm( + Vec::::default(), + FUNC_NAME, + META_WASM_V1.to_vec(), + state_args!(), + ) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().first().cloned(); @@ -128,6 +142,7 @@ fn read_state_with_parameterized_wasm_func_returns_transformed_state() { let actual_state: Option = program .read_state_using_wasm( + Vec::::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args!(other_person.clone()), @@ -153,6 +168,7 @@ fn read_state_with_two_args_wasm_func_returns_transformed_state() { let actual_state: Option = program .read_state_using_wasm( + Vec::::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args!(name.clone(), surname.clone()), diff --git a/examples/node/Cargo.toml b/examples/node/Cargo.toml index 097453824e4..d0e5078b4f4 100644 --- a/examples/node/Cargo.toml +++ b/examples/node/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-node" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -17,8 +18,6 @@ gear-wasm-builder.workspace = true gstd = { workspace = true, features = ["debug"] } gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/node/src/lib.rs b/examples/node/src/lib.rs index ee8ff58f89e..1b64af59594 100644 --- a/examples/node/src/lib.rs +++ b/examples/node/src/lib.rs @@ -20,8 +20,7 @@ extern crate alloc; -use alloc::collections::BTreeSet; -use gstd::{debug, exec, msg, prelude::*, ActorId, MessageId}; +use gstd::ActorId; use parity_scale_codec::{Decode, Encode}; #[cfg(feature = "std")] @@ -56,233 +55,8 @@ pub enum Reply { Failure, } -enum TransitionState { - Ready, - NotReady, - Failed, -} - -struct Transition { - #[allow(dead_code)] - to_status: u32, - origin: ActorId, - query_list: Vec, - message_id: MessageId, - last_sent_message_id: MessageId, - query_index: usize, - state: TransitionState, -} - -struct NodeState { - #[allow(dead_code)] - status: u32, - sub_nodes: BTreeSet, - transition: Option, -} - -static mut STATE: Option = None; - -#[no_mangle] -extern "C" fn handle() { - let reply = match msg::load() { - Ok(request) => process(request), - Err(e) => { - debug!("Error processing request: {e:?}"); - Reply::Failure - } - }; - - msg::reply(reply, 0).unwrap(); -} - -fn state() -> &'static mut NodeState { - unsafe { STATE.as_mut().unwrap() } -} - -fn process(request: Request) -> Reply { - if let Some(mut transition) = state().transition.take() { - if transition.message_id == msg::id() { - // one of the answers has set failed state - if let TransitionState::Failed = transition.state { - return Reply::Failure; - } - - // this means that we received replies from all subnodes - if transition.query_index == transition.query_list.len() { - match transition.state { - TransitionState::NotReady => { - transition.state = TransitionState::Ready; - - debug!("Returning final ready signal"); - - // this is ready to further process with committing - state().transition = Some(transition); - return Reply::Success; - } - TransitionState::Ready => { - // this means we successfully committed and we can - // drop the transition returning success - debug!("Returning final commit signal"); - - return Reply::Success; - } - _ => { - // this is some invalid state already - return Reply::Failure; - } - } - } - - // this means we need to send another sub-node query - let next_sub_node = transition - .query_list - .get(transition.query_index) - .expect("Checked above that it has that number of elements; qed"); - - transition.last_sent_message_id = msg::send(*next_sub_node, request, 0).unwrap(); - - state().transition = Some(transition); - - exec::wait(); - } else { - // this is just a new message that should be processed normally, without continuation. - state().transition = Some(transition); - } - } - - match request { - Request::IsReady => { - if state().transition.is_none() { - Reply::Yes - } else { - Reply::No - } - } - Request::Begin(Operation { to_status }) => { - if state().transition.is_some() { - Reply::Failure - } else { - let mut transition = Transition { - to_status, - origin: msg::source(), - query_index: 0, - query_list: vec![], - state: TransitionState::NotReady, - message_id: msg::id(), - last_sent_message_id: MessageId::default(), - }; - - debug!("Transition started"); - - if !state().sub_nodes.is_empty() { - debug!("Transition started is complex"); - - transition.query_list = state().sub_nodes.iter().cloned().collect(); - let first_sub_node = *transition - .query_list - .get(0) - .expect("Checked above that sub_nodes is not empty; qed"); - transition.last_sent_message_id = - msg::send(first_sub_node, request, 0).unwrap(); - state().transition = Some(transition); - exec::wait() - } else { - transition.state = TransitionState::Ready; - state().transition = Some(transition); - Reply::Success - } - } - } - Request::Commit => { - if state().sub_nodes.is_empty() { - let (transition, reply) = match state().transition.take() { - Some(transition) => { - if transition.origin != msg::source() { - (Some(transition), Reply::Failure) - } else { - (None, Reply::Success) - } - } - None => (None, Reply::Failure), - }; - - state().transition = transition; - - reply - } else if let Some(mut transition) = state().transition.take() { - if let TransitionState::Ready = transition.state { - let first_sub_node = *transition - .query_list - .get(0) - .expect("Checked above that sub_nodes is not empty; qed"); - - transition.query_index = 0; - - transition.message_id = msg::id(); - - transition.last_sent_message_id = - msg::send(first_sub_node, request, 0).unwrap(); - - state().transition = Some(transition); - - exec::wait() - } else { - debug!("Returning failure because current state is not READY"); - Reply::Failure - } - } else { - debug!("Returning failure because there is no transition in process"); - Reply::Failure - } - } - Request::Add(sub_node) => { - state().sub_nodes.insert(sub_node); - Reply::Success - } - } -} - -#[no_mangle] -extern "C" fn handle_reply() { - if let Some(ref mut transition) = state().transition { - if msg::reply_to().unwrap() != transition.last_sent_message_id { - return; - } - - match msg::load() { - Ok(reply) => { - transition.query_index += 1; - if let Reply::Success = reply { - } else { - transition.state = TransitionState::Failed; - } - exec::wake(transition.message_id).unwrap(); - } - Err(e) => { - transition.state = TransitionState::Failed; - debug!("Error processing reply: {e:?}"); - exec::wake(transition.message_id).unwrap(); - } - } - } else { - debug!("Got some reply that can not be processed"); - } -} - -#[no_mangle] -extern "C" fn init() { - let init: Initialization = msg::load().expect("Failed to decode init"); - - unsafe { - STATE = Some(NodeState { - status: init.status, - sub_nodes: BTreeSet::default(), - transition: None, - }); - } - - msg::reply((), 0).unwrap(); -} +#[cfg(not(feature = "std"))] +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/node/src/wasm.rs b/examples/node/src/wasm.rs new file mode 100644 index 00000000000..83c9acefa07 --- /dev/null +++ b/examples/node/src/wasm.rs @@ -0,0 +1,248 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{Initialization, Operation, Reply, Request}; +use gstd::{collections::BTreeSet, debug, exec, msg, prelude::*, ActorId, MessageId}; + +static mut STATE: Option = None; + +enum TransitionState { + Ready, + NotReady, + Failed, +} + +struct Transition { + #[allow(dead_code)] + to_status: u32, + origin: ActorId, + query_list: Vec, + message_id: MessageId, + last_sent_message_id: MessageId, + query_index: usize, + state: TransitionState, +} + +struct NodeState { + #[allow(dead_code)] + status: u32, + sub_nodes: BTreeSet, + transition: Option, +} + +#[no_mangle] +extern "C" fn handle() { + let reply = match msg::load() { + Ok(request) => process(request), + Err(e) => { + debug!("Error processing request: {e:?}"); + Reply::Failure + } + }; + + msg::reply(reply, 0).unwrap(); +} + +fn state() -> &'static mut NodeState { + unsafe { STATE.as_mut().unwrap() } +} + +fn process(request: Request) -> Reply { + if let Some(mut transition) = state().transition.take() { + if transition.message_id == msg::id() { + // one of the answers has set failed state + if let TransitionState::Failed = transition.state { + return Reply::Failure; + } + + // this means that we received replies from all subnodes + if transition.query_index == transition.query_list.len() { + match transition.state { + TransitionState::NotReady => { + transition.state = TransitionState::Ready; + + debug!("Returning final ready signal"); + + // this is ready to further process with committing + state().transition = Some(transition); + return Reply::Success; + } + TransitionState::Ready => { + // this means we successfully committed and we can + // drop the transition returning success + debug!("Returning final commit signal"); + + return Reply::Success; + } + _ => { + // this is some invalid state already + return Reply::Failure; + } + } + } + + // this means we need to send another sub-node query + let next_sub_node = transition + .query_list + .get(transition.query_index) + .expect("Checked above that it has that number of elements; qed"); + + transition.last_sent_message_id = msg::send(*next_sub_node, request, 0).unwrap(); + + state().transition = Some(transition); + + exec::wait(); + } else { + // this is just a new message that should be processed normally, without continuation. + state().transition = Some(transition); + } + } + + match request { + Request::IsReady => { + if state().transition.is_none() { + Reply::Yes + } else { + Reply::No + } + } + Request::Begin(Operation { to_status }) => { + if state().transition.is_some() { + Reply::Failure + } else { + let mut transition = Transition { + to_status, + origin: msg::source(), + query_index: 0, + query_list: vec![], + state: TransitionState::NotReady, + message_id: msg::id(), + last_sent_message_id: MessageId::default(), + }; + + debug!("Transition started"); + + if !state().sub_nodes.is_empty() { + debug!("Transition started is complex"); + + transition.query_list = state().sub_nodes.iter().cloned().collect(); + let first_sub_node = *transition + .query_list + .get(0) + .expect("Checked above that sub_nodes is not empty; qed"); + transition.last_sent_message_id = + msg::send(first_sub_node, request, 0).unwrap(); + state().transition = Some(transition); + exec::wait() + } else { + transition.state = TransitionState::Ready; + state().transition = Some(transition); + Reply::Success + } + } + } + Request::Commit => { + if state().sub_nodes.is_empty() { + let (transition, reply) = match state().transition.take() { + Some(transition) => { + if transition.origin != msg::source() { + (Some(transition), Reply::Failure) + } else { + (None, Reply::Success) + } + } + None => (None, Reply::Failure), + }; + + state().transition = transition; + + reply + } else if let Some(mut transition) = state().transition.take() { + if let TransitionState::Ready = transition.state { + let first_sub_node = *transition + .query_list + .get(0) + .expect("Checked above that sub_nodes is not empty; qed"); + + transition.query_index = 0; + + transition.message_id = msg::id(); + + transition.last_sent_message_id = + msg::send(first_sub_node, request, 0).unwrap(); + + state().transition = Some(transition); + + exec::wait() + } else { + debug!("Returning failure because current state is not READY"); + Reply::Failure + } + } else { + debug!("Returning failure because there is no transition in process"); + Reply::Failure + } + } + Request::Add(sub_node) => { + state().sub_nodes.insert(sub_node); + Reply::Success + } + } +} + +#[no_mangle] +extern "C" fn handle_reply() { + if let Some(ref mut transition) = state().transition { + if msg::reply_to().unwrap() != transition.last_sent_message_id { + return; + } + + match msg::load() { + Ok(reply) => { + transition.query_index += 1; + if let Reply::Success = reply { + } else { + transition.state = TransitionState::Failed; + } + exec::wake(transition.message_id).unwrap(); + } + Err(e) => { + transition.state = TransitionState::Failed; + debug!("Error processing reply: {e:?}"); + exec::wake(transition.message_id).unwrap(); + } + } + } else { + debug!("Got some reply that can not be processed"); + } +} + +#[no_mangle] +extern "C" fn init() { + let init: Initialization = msg::load().expect("Failed to decode init"); + + unsafe { + STATE = Some(NodeState { + status: init.status, + sub_nodes: BTreeSet::default(), + transition: None, + }); + } + + msg::reply((), 0).unwrap(); +} diff --git a/examples/out-of-memory/Cargo.toml b/examples/out-of-memory/Cargo.toml index ac2f3feaa37..3b039d61913 100644 --- a/examples/out-of-memory/Cargo.toml +++ b/examples/out-of-memory/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-out-of-memory" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/out-of-memory/src/lib.rs b/examples/out-of-memory/src/lib.rs index ca12845c8fc..b64f1e21f2f 100644 --- a/examples/out-of-memory/src/lib.rs +++ b/examples/out-of-memory/src/lib.rs @@ -29,16 +29,5 @@ mod code { #[cfg(feature = "std")] pub use code::WASM_BINARY_OPT as WASM_BINARY; -#[cfg(target_arch = "wasm32")] -mod wasm { - use alloc::alloc::Layout; - - #[no_mangle] - extern "C" fn init() { - unsafe { - // Force rustc not to remove memory import - *(10usize as *mut u8) = 10; - } - alloc::alloc::handle_alloc_error(Layout::new::<[u8; 64 * 1024]>()); - } -} +#[cfg(not(feature = "std"))] +mod wasm; diff --git a/examples/out-of-memory/src/wasm.rs b/examples/out-of-memory/src/wasm.rs new file mode 100644 index 00000000000..6ac5382b67a --- /dev/null +++ b/examples/out-of-memory/src/wasm.rs @@ -0,0 +1,28 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use alloc::alloc::Layout; + +#[no_mangle] +extern "C" fn init() { + unsafe { + // Force rustc not to remove memory import + *(10usize as *mut u8) = 10; + } + alloc::alloc::handle_alloc_error(Layout::new::<[u8; 64 * 1024]>()); +} diff --git a/examples/piggy-bank/Cargo.toml b/examples/piggy-bank/Cargo.toml index 77e2791deae..12e17ebc57b 100644 --- a/examples/piggy-bank/Cargo.toml +++ b/examples/piggy-bank/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/piggy-bank/src/lib.rs b/examples/piggy-bank/src/lib.rs index 4ddfa5a7498..fbc08d47a0b 100644 --- a/examples/piggy-bank/src/lib.rs +++ b/examples/piggy-bank/src/lib.rs @@ -27,20 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{debug, exec, msg}; - - #[no_mangle] - extern "C" fn handle() { - msg::with_read_on_stack(|msg| { - let available_value = exec::value_available(); - let value = msg::value(); - debug!("inserted: {value}, total: {available_value}"); - - if msg.expect("Failed to load payload bytes") == b"smash" { - debug!("smashing, total: {available_value}"); - msg::reply_bytes(b"send", available_value).unwrap(); - } - }); - } -} +mod wasm; diff --git a/examples/piggy-bank/src/wasm.rs b/examples/piggy-bank/src/wasm.rs new file mode 100644 index 00000000000..cc8ea523c1c --- /dev/null +++ b/examples/piggy-bank/src/wasm.rs @@ -0,0 +1,33 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{debug, exec, msg}; + +#[no_mangle] +extern "C" fn handle() { + msg::with_read_on_stack(|msg| { + let available_value = exec::value_available(); + let value = msg::value(); + debug!("inserted: {value}, total: {available_value}"); + + if msg.expect("Failed to load payload bytes") == b"smash" { + debug!("smashing, total: {available_value}"); + msg::reply_bytes(b"send", available_value).unwrap(); + } + }); +} diff --git a/examples/ping/Cargo.toml b/examples/ping/Cargo.toml index fa79445e23f..50dd1ce8605 100644 --- a/examples/ping/Cargo.toml +++ b/examples/ping/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/ping/src/lib.rs b/examples/ping/src/lib.rs index 0c48715a6b0..fbc08d47a0b 100644 --- a/examples/ping/src/lib.rs +++ b/examples/ping/src/lib.rs @@ -27,15 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, prelude::*}; - - #[no_mangle] - extern "C" fn handle() { - let payload = msg::load_bytes().expect("Failed to load payload"); - - if payload == b"PING" { - msg::reply_bytes("PONG", 0).expect("Failed to send reply"); - } - } -} +mod wasm; diff --git a/examples/ping/src/wasm.rs b/examples/ping/src/wasm.rs new file mode 100644 index 00000000000..69b988852d2 --- /dev/null +++ b/examples/ping/src/wasm.rs @@ -0,0 +1,28 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{msg, prelude::*}; + +#[no_mangle] +extern "C" fn handle() { + let payload = msg::load_bytes().expect("Failed to load payload"); + + if payload == b"PING" { + msg::reply_bytes("PONG", 0).expect("Failed to send reply"); + } +} diff --git a/examples/program-factory/Cargo.toml b/examples/program-factory/Cargo.toml index 09539fd458c..104d6b64ff3 100644 --- a/examples/program-factory/Cargo.toml +++ b/examples/program-factory/Cargo.toml @@ -2,14 +2,15 @@ name = "demo-program-factory" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true -hex-literal = "*" +parity-scale-codec.workspace = true +hex-literal.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -17,8 +18,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/program-factory/src/lib.rs b/examples/program-factory/src/lib.rs index 2cb14dfee0e..9a919f1cd11 100644 --- a/examples/program-factory/src/lib.rs +++ b/examples/program-factory/src/lib.rs @@ -48,60 +48,7 @@ const CHILD_CODE_HASH: [u8; 32] = hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a"); #[cfg(not(feature = "std"))] -mod wasm { - use super::{CreateProgram, CHILD_CODE_HASH}; - use gstd::{msg, prog, ActorId}; - - static mut COUNTER: i32 = 0; - static mut ORIGIN: Option = None; - - #[no_mangle] - extern "C" fn init() { - unsafe { ORIGIN = Some(msg::source()) }; - } - - #[no_mangle] - extern "C" fn handle() { - match msg::load().expect("provided invalid payload") { - CreateProgram::Default => { - let submitted_code = CHILD_CODE_HASH.into(); - let (_message_id, new_program_id) = prog::create_program_bytes_with_gas( - submitted_code, - unsafe { COUNTER.to_le_bytes() }, - [], - 10_000_000_000, - 0, - ) - .unwrap(); - msg::send_bytes(new_program_id, [], 0).unwrap(); - - unsafe { COUNTER += 1 }; - } - CreateProgram::Custom(custom_child_data) => { - for (code_hash, salt, gas_limit) in custom_child_data { - let submitted_code = code_hash.into(); - let (_message_id, new_program_id) = prog::create_program_bytes_with_gas( - submitted_code, - &salt, - [], - gas_limit, - 0, - ) - .unwrap(); - msg::send_bytes(new_program_id, [], 0).expect("Failed to send message"); - } - } - }; - } - - #[no_mangle] - extern "C" fn handle_reply() { - if !msg::reply_code().unwrap().is_success() { - let origin = unsafe { ORIGIN.unwrap() }; - msg::send_bytes(origin, [], 0).unwrap(); - } - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/program-factory/src/wasm.rs b/examples/program-factory/src/wasm.rs new file mode 100644 index 00000000000..f0cd48f1db5 --- /dev/null +++ b/examples/program-factory/src/wasm.rs @@ -0,0 +1,71 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! An example of `create_program_with_gas` sys-call. +//! +//! The program is mainly used for testing the sys-call logic in pallet `gear` tests. +//! It works as a program factory: depending on input type it sends program creation +//! request (message). + +use crate::{CreateProgram, CHILD_CODE_HASH}; +use gstd::{msg, prog, ActorId}; + +static mut COUNTER: i32 = 0; +static mut ORIGIN: Option = None; + +#[no_mangle] +extern "C" fn init() { + unsafe { ORIGIN = Some(msg::source()) }; +} + +#[no_mangle] +extern "C" fn handle() { + match msg::load().expect("provided invalid payload") { + CreateProgram::Default => { + let submitted_code = CHILD_CODE_HASH.into(); + let (_message_id, new_program_id) = prog::create_program_bytes_with_gas( + submitted_code, + unsafe { COUNTER.to_le_bytes() }, + [], + 10_000_000_000, + 0, + ) + .unwrap(); + msg::send_bytes(new_program_id, [], 0).unwrap(); + + unsafe { COUNTER += 1 }; + } + CreateProgram::Custom(custom_child_data) => { + for (code_hash, salt, gas_limit) in custom_child_data { + let submitted_code = code_hash.into(); + let (_message_id, new_program_id) = + prog::create_program_bytes_with_gas(submitted_code, &salt, [], gas_limit, 0) + .unwrap(); + msg::send_bytes(new_program_id, [], 0).expect("Failed to send message"); + } + } + }; +} + +#[no_mangle] +extern "C" fn handle_reply() { + if !msg::reply_code().unwrap().is_success() { + let origin = unsafe { ORIGIN.unwrap() }; + msg::send_bytes(origin, [], 0).unwrap(); + } +} diff --git a/examples/program-generator/Cargo.toml b/examples/program-generator/Cargo.toml index d3c686e1030..9782f2a468f 100644 --- a/examples/program-generator/Cargo.toml +++ b/examples/program-generator/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/program-generator/src/lib.rs b/examples/program-generator/src/lib.rs index aaf1295970a..4333e8bd24e 100644 --- a/examples/program-generator/src/lib.rs +++ b/examples/program-generator/src/lib.rs @@ -38,33 +38,4 @@ pub const CHILD_WAT: &str = r#" "#; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{collections::BTreeSet, prelude::*, prog::ProgramGenerator, CodeId}; - - fn check_salt_uniqueness() { - let salts: Vec<_> = (0..10).map(|_| ProgramGenerator::get_salt()).collect(); - let salts_len = salts.len(); - - // The set's length should be equal to the vector's one - // if there are no repetitive values. - let salts_set: BTreeSet<_> = salts.into_iter().collect(); - assert_eq!(salts_len, salts_set.len()); - } - - #[no_mangle] - extern "C" fn handle() { - let submitted_code: CodeId = - hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a") - .into(); - - ProgramGenerator::create_program_bytes_with_gas( - submitted_code, - b"payload", - 10_000_000_000, - 0, - ) - .unwrap(); - - check_salt_uniqueness(); - } -} +mod wasm; diff --git a/examples/program-generator/src/wasm.rs b/examples/program-generator/src/wasm.rs new file mode 100644 index 00000000000..cdf11b1a684 --- /dev/null +++ b/examples/program-generator/src/wasm.rs @@ -0,0 +1,41 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{collections::BTreeSet, prelude::*, prog::ProgramGenerator, CodeId}; + +fn check_salt_uniqueness() { + let salts: Vec<_> = (0..10).map(|_| ProgramGenerator::get_salt()).collect(); + let salts_len = salts.len(); + + // The set's length should be equal to the vector's one + // if there are no repetitive values. + let salts_set: BTreeSet<_> = salts.into_iter().collect(); + assert_eq!(salts_len, salts_set.len()); +} + +#[no_mangle] +extern "C" fn handle() { + let submitted_code: CodeId = + hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a") + .into(); + + ProgramGenerator::create_program_bytes_with_gas(submitted_code, b"payload", 10_000_000_000, 0) + .unwrap(); + + check_salt_uniqueness(); +} diff --git a/examples/proxy-relay/Cargo.toml b/examples/proxy-relay/Cargo.toml index b12547ba515..4378c75e4e3 100644 --- a/examples/proxy-relay/Cargo.toml +++ b/examples/proxy-relay/Cargo.toml @@ -2,20 +2,19 @@ name = "demo-proxy-relay" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true -scale-info = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true +scale-info.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std", "scale-info/std"] diff --git a/examples/proxy-relay/src/lib.rs b/examples/proxy-relay/src/lib.rs index 4852a5435f1..00ff90ce91d 100644 --- a/examples/proxy-relay/src/lib.rs +++ b/examples/proxy-relay/src/lib.rs @@ -49,84 +49,4 @@ pub enum RelayCall { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gstd::msg::{self, MessageHandle}; - - static mut RELAY_CALL: Option = None; - - fn resend_push(resend_pushes: &[ResendPushData]) { - for data in resend_pushes { - let msg_handle = MessageHandle::init().expect("Failed to obtain new message handle"); - - let ResendPushData { - destination, - start, - end, - } = data; - - let end = end.map(|(e, flag)| (e as usize, flag)); - match start.map(|s| s as usize) { - Some(s) => match end { - None => { - msg_handle.push_input(s..).expect("Push failed"); - } - Some((e, true)) => { - msg_handle.push_input(s..=e).expect("Push failed"); - } - Some((e, _)) => { - msg_handle.push_input(s..e).expect("Push failed"); - } - }, - None => match end { - None => { - msg_handle.push_input(..).expect("Push failed"); - } - Some((e, true)) => { - msg_handle.push_input(..=e).expect("Push failed"); - } - Some((e, _)) => { - msg_handle.push_input(..e).expect("Push failed"); - } - }, - } - - msg_handle - .commit(*destination, msg::value()) - .expect("Commit failed"); - } - } - - #[no_mangle] - extern "C" fn handle() { - use RelayCall::*; - let relay_call = unsafe { RELAY_CALL.as_ref().expect("Relay call is not initialized") }; - - match relay_call { - Resend(d) => { - msg::send_input(*d, msg::value(), ..msg::size()).expect("Resend failed"); - } - ResendWithGas(d, gas) => { - msg::send_input_with_gas(*d, *gas, msg::value(), ..).expect("Resend wgas failed"); - } - ResendPush(data) => { - resend_push(data); - } - Rereply => { - msg::reply_input(msg::value(), 0..msg::size()).expect("Rereply failed"); - } - RereplyPush => { - msg::reply_push_input(0..).expect("Push failed"); - msg::reply_commit(msg::value()).expect("Commit failed"); - } - RereplyWithGas(gas) => { - msg::reply_input_with_gas(*gas, msg::value(), ..).expect("Rereply wgas failed"); - } - } - } - - #[no_mangle] - extern "C" fn init() { - unsafe { RELAY_CALL = Some(msg::load().expect("Failed to decode `RelayCall'")) }; - } -} +mod wasm; diff --git a/examples/proxy-relay/src/wasm.rs b/examples/proxy-relay/src/wasm.rs new file mode 100644 index 00000000000..8e5f1594df4 --- /dev/null +++ b/examples/proxy-relay/src/wasm.rs @@ -0,0 +1,97 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{RelayCall, ResendPushData}; +use gstd::msg::{self, MessageHandle}; + +static mut RELAY_CALL: Option = None; + +fn resend_push(resend_pushes: &[ResendPushData]) { + for data in resend_pushes { + let msg_handle = MessageHandle::init().expect("Failed to obtain new message handle"); + + let ResendPushData { + destination, + start, + end, + } = data; + + let end = end.map(|(e, flag)| (e as usize, flag)); + match start.map(|s| s as usize) { + Some(s) => match end { + None => { + msg_handle.push_input(s..).expect("Push failed"); + } + Some((e, true)) => { + msg_handle.push_input(s..=e).expect("Push failed"); + } + Some((e, _)) => { + msg_handle.push_input(s..e).expect("Push failed"); + } + }, + None => match end { + None => { + msg_handle.push_input(..).expect("Push failed"); + } + Some((e, true)) => { + msg_handle.push_input(..=e).expect("Push failed"); + } + Some((e, _)) => { + msg_handle.push_input(..e).expect("Push failed"); + } + }, + } + + msg_handle + .commit(*destination, msg::value()) + .expect("Commit failed"); + } +} + +#[no_mangle] +extern "C" fn handle() { + use RelayCall::*; + let relay_call = unsafe { RELAY_CALL.as_ref().expect("Relay call is not initialized") }; + + match relay_call { + Resend(d) => { + msg::send_input(*d, msg::value(), ..msg::size()).expect("Resend failed"); + } + ResendWithGas(d, gas) => { + msg::send_input_with_gas(*d, *gas, msg::value(), ..).expect("Resend wgas failed"); + } + ResendPush(data) => { + resend_push(data); + } + Rereply => { + msg::reply_input(msg::value(), 0..msg::size()).expect("Rereply failed"); + } + RereplyPush => { + msg::reply_push_input(0..).expect("Push failed"); + msg::reply_commit(msg::value()).expect("Commit failed"); + } + RereplyWithGas(gas) => { + msg::reply_input_with_gas(*gas, msg::value(), ..).expect("Rereply wgas failed"); + } + } +} + +#[no_mangle] +extern "C" fn init() { + unsafe { RELAY_CALL = Some(msg::load().expect("Failed to decode `RelayCall'")) }; +} diff --git a/examples/proxy-reservation-with-gas/Cargo.toml b/examples/proxy-reservation-with-gas/Cargo.toml index 4371dac5811..c8e775cdf43 100644 --- a/examples/proxy-reservation-with-gas/Cargo.toml +++ b/examples/proxy-reservation-with-gas/Cargo.toml @@ -2,20 +2,19 @@ name = "demo-proxy-reservation-with-gas" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true -scale-info = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true +scale-info.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std", "scale-info/std"] diff --git a/examples/proxy-reservation-with-gas/src/lib.rs b/examples/proxy-reservation-with-gas/src/lib.rs index 00a5c6f8a6a..427db5013d6 100644 --- a/examples/proxy-reservation-with-gas/src/lib.rs +++ b/examples/proxy-reservation-with-gas/src/lib.rs @@ -37,35 +37,4 @@ pub struct InputArgs { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::InputArgs; - use gstd::{msg, ActorId, ReservationId}; - - static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); - static mut DELAY: u32 = 0; - static mut RESERVATION_AMOUNT: u64 = 0; - - #[no_mangle] - extern "C" fn handle() { - let reservation_id = ReservationId::reserve(unsafe { RESERVATION_AMOUNT }, 80) - .expect("Failed to reserve gas"); - msg::send_delayed_from_reservation( - reservation_id, - unsafe { DESTINATION }, - b"proxied message", - msg::value(), - unsafe { DELAY }, - ) - .expect("Failed to proxy message"); - } - - #[no_mangle] - extern "C" fn init() { - let args: InputArgs = msg::load().expect("Failed to decode `InputArgs'"); - unsafe { - DESTINATION = args.destination; - DELAY = args.delay; - RESERVATION_AMOUNT = args.reservation_amount; - } - } -} +mod wasm; diff --git a/examples/proxy-reservation-with-gas/src/wasm.rs b/examples/proxy-reservation-with-gas/src/wasm.rs new file mode 100644 index 00000000000..695e6678d4b --- /dev/null +++ b/examples/proxy-reservation-with-gas/src/wasm.rs @@ -0,0 +1,48 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::InputArgs; +use gstd::{msg, ActorId, ReservationId}; + +static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); +static mut DELAY: u32 = 0; +static mut RESERVATION_AMOUNT: u64 = 0; + +#[no_mangle] +extern "C" fn handle() { + let reservation_id = + ReservationId::reserve(unsafe { RESERVATION_AMOUNT }, 80).expect("Failed to reserve gas"); + msg::send_delayed_from_reservation( + reservation_id, + unsafe { DESTINATION }, + b"proxied message", + msg::value(), + unsafe { DELAY }, + ) + .expect("Failed to proxy message"); +} + +#[no_mangle] +extern "C" fn init() { + let args: InputArgs = msg::load().expect("Failed to decode `InputArgs'"); + unsafe { + DESTINATION = args.destination; + DELAY = args.delay; + RESERVATION_AMOUNT = args.reservation_amount; + } +} diff --git a/examples/proxy/Cargo.toml b/examples/proxy/Cargo.toml index e3603c7e609..f679ca89cd5 100644 --- a/examples/proxy/Cargo.toml +++ b/examples/proxy/Cargo.toml @@ -2,20 +2,19 @@ name = "demo-proxy" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -gstd = { workspace = true } -parity-scale-codec = { workspace = true, features = ["derive"] } -scale-info = { workspace = true, features = ["derive"] } +gstd.workspace = true +parity-scale-codec.workspace = true +scale-info.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] wasm-wrapper = [] diff --git a/examples/proxy/src/lib.rs b/examples/proxy/src/lib.rs index 91cc0648d14..3afdf022b59 100644 --- a/examples/proxy/src/lib.rs +++ b/examples/proxy/src/lib.rs @@ -37,28 +37,4 @@ pub struct InputArgs { pub const HANDLE_REPLY_EXPECT: &str = "I will fail"; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - use crate::{InputArgs, HANDLE_REPLY_EXPECT}; - use gstd::{msg, ActorId}; - - static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); - - #[no_mangle] - extern "C" fn init() { - let args: InputArgs = msg::load().expect("Failed to decode `InputArgs'"); - unsafe { DESTINATION = args.destination.into() }; - } - - #[no_mangle] - extern "C" fn handle() { - let payload = msg::load_bytes().expect("failed to load bytes"); - msg::send_bytes(unsafe { DESTINATION }, payload, msg::value()) - .expect("failed to send bytes"); - } - - #[no_mangle] - extern "C" fn handle_reply() { - // Will panic here as replies denied in `handle_reply`. - msg::reply_bytes([], 0).expect(HANDLE_REPLY_EXPECT); - } -} +mod wasm; diff --git a/examples/proxy/src/wasm.rs b/examples/proxy/src/wasm.rs new file mode 100644 index 00000000000..b4c78b974c3 --- /dev/null +++ b/examples/proxy/src/wasm.rs @@ -0,0 +1,40 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{InputArgs, HANDLE_REPLY_EXPECT}; +use gstd::{msg, ActorId}; + +static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); + +#[no_mangle] +extern "C" fn init() { + let args: InputArgs = msg::load().expect("Failed to decode `InputArgs'"); + unsafe { DESTINATION = args.destination.into() }; +} + +#[no_mangle] +extern "C" fn handle() { + let payload = msg::load_bytes().expect("failed to load bytes"); + msg::send_bytes(unsafe { DESTINATION }, payload, msg::value()).expect("failed to send bytes"); +} + +#[no_mangle] +extern "C" fn handle_reply() { + // Will panic here as replies denied in `handle_reply`. + msg::reply_bytes([], 0).expect(HANDLE_REPLY_EXPECT); +} diff --git a/examples/read-big-state/Cargo.toml b/examples/read-big-state/Cargo.toml index 120c31ee6f2..c20badfe83a 100644 --- a/examples/read-big-state/Cargo.toml +++ b/examples/read-big-state/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/read-big-state/src/lib.rs b/examples/read-big-state/src/lib.rs index 3f7d147551c..81cf6042e3b 100644 --- a/examples/read-big-state/src/lib.rs +++ b/examples/read-big-state/src/lib.rs @@ -60,24 +60,4 @@ impl State { } #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - use super::*; - use gstd::{msg, prelude::*}; - - static mut STATE: Option = None; - - fn state_mut() -> &'static mut State { - unsafe { STATE.get_or_insert_with(State::new) } - } - - #[no_mangle] - extern "C" fn handle() { - let strings = msg::load().expect("Failed to load state"); - state_mut().insert(strings); - } - - #[no_mangle] - extern "C" fn state() { - msg::reply(state_mut(), 0).expect("Error in reply of state"); - } -} +mod wasm; diff --git a/examples/read-big-state/src/wasm.rs b/examples/read-big-state/src/wasm.rs new file mode 100644 index 00000000000..65f13db548c --- /dev/null +++ b/examples/read-big-state/src/wasm.rs @@ -0,0 +1,37 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::State; +use gstd::{msg, prelude::*}; + +static mut STATE: Option = None; + +fn state_mut() -> &'static mut State { + unsafe { STATE.get_or_insert_with(State::new) } +} + +#[no_mangle] +extern "C" fn handle() { + let strings = msg::load().expect("Failed to load state"); + state_mut().insert(strings); +} + +#[no_mangle] +extern "C" fn state() { + msg::reply(state_mut(), 0).expect("Error in reply of state"); +} diff --git a/examples/reservation-manager/Cargo.toml b/examples/reservation-manager/Cargo.toml index df7f282d5ff..372ecdbd5aa 100644 --- a/examples/reservation-manager/Cargo.toml +++ b/examples/reservation-manager/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/reservation-manager/src/lib.rs b/examples/reservation-manager/src/lib.rs index c6a1ddf5522..9e8281a1918 100644 --- a/examples/reservation-manager/src/lib.rs +++ b/examples/reservation-manager/src/lib.rs @@ -35,33 +35,4 @@ pub enum Action { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gstd::{msg, prelude::*, Reservations}; - - static mut RESERVATIONS: Reservations = Reservations::new(); - - #[no_mangle] - extern "C" fn handle() { - let action: Action = msg::load().expect("Failed to load message"); - - unsafe { - match action { - Action::Reserve { amount, duration } => { - RESERVATIONS - .reserve(amount, duration) - .expect("Failed to reserve gas"); - } - Action::SendMessageFromReservation { gas_amount } => { - let reservation = RESERVATIONS.try_take_reservation(gas_amount); - if let Some(reservation) = reservation { - msg::send_bytes_from_reservation(reservation.id(), msg::source(), [], 0) - .expect("Failed to send message from reservation"); - } else { - panic!("Reservation not found"); - } - } - } - } - } -} +mod wasm; diff --git a/examples/reservation-manager/src/wasm.rs b/examples/reservation-manager/src/wasm.rs new file mode 100644 index 00000000000..a9d81564c0b --- /dev/null +++ b/examples/reservation-manager/src/wasm.rs @@ -0,0 +1,46 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Action; +use gstd::{msg, prelude::*, Reservations}; + +static mut RESERVATIONS: Reservations = Reservations::new(); + +#[no_mangle] +extern "C" fn handle() { + let action: Action = msg::load().expect("Failed to load message"); + + unsafe { + match action { + Action::Reserve { amount, duration } => { + RESERVATIONS + .reserve(amount, duration) + .expect("Failed to reserve gas"); + } + Action::SendMessageFromReservation { gas_amount } => { + let reservation = RESERVATIONS.try_take_reservation(gas_amount); + if let Some(reservation) = reservation { + msg::send_bytes_from_reservation(reservation.id(), msg::source(), [], 0) + .expect("Failed to send message from reservation"); + } else { + panic!("Reservation not found"); + } + } + } + } +} diff --git a/examples/reserve-gas/Cargo.toml b/examples/reserve-gas/Cargo.toml index 132b43911fa..896a36a5d0b 100644 --- a/examples/reserve-gas/Cargo.toml +++ b/examples/reserve-gas/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/reserve-gas/src/lib.rs b/examples/reserve-gas/src/lib.rs index 7644c983bb7..ca903da75b8 100644 --- a/examples/reserve-gas/src/lib.rs +++ b/examples/reserve-gas/src/lib.rs @@ -63,195 +63,7 @@ pub type GasAmount = u64; pub type BlockCount = u32; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - use super::*; - use gstd::{ - errors::{Error, ExecutionError, ExtError, ReservationError}, - exec, msg, - prelude::*, - MessageId, ReservationId, - }; - - static mut RESERVATION_ID: Option = None; - static mut RESERVATIONS: Vec = Vec::new(); - static mut INIT_MSG: MessageId = MessageId::new([0; 32]); - static mut WAKE_STATE: WakeState = WakeState::Initial; - - #[derive(Debug, Eq, PartialEq)] - enum WakeState { - Initial, - Panic, - Exit, - } - - #[no_mangle] - extern "C" fn init() { - unsafe { INIT_MSG = msg::id() }; - - let action: InitAction = msg::load().unwrap(); - - match action { - InitAction::Normal(ref reservations) => { - for (amount, duration) in reservations { - let _ = ReservationId::reserve(*amount, *duration).expect("reservation"); - } - - // no actual reservation and unreservation is occurred - let noop_reservation = - ReservationId::reserve(50_000, 10).expect("noop reservation"); - let unreserved_amount = noop_reservation.unreserve().expect("noop unreservation"); - assert_eq!(unreserved_amount, 50_000); - - unsafe { - RESERVATION_ID = Some( - ReservationId::reserve(RESERVATION_AMOUNT, 15) - .expect("reservation across executions"), - ) - }; - } - InitAction::Wait => match unsafe { &WAKE_STATE } { - WakeState::Initial => { - let _reservation = ReservationId::reserve(50_000, 10); - // to find message to reply to in test - msg::send(msg::source(), (), 0).unwrap(); - exec::wait(); - } - WakeState::Panic => { - panic!() - } - WakeState::Exit => { - exec::exit(msg::source()); - } - }, - InitAction::CheckArgs { mailbox_threshold } => { - assert_eq!( - ReservationId::reserve(0, 10), - Err(Error::Core( - ExtError::Reservation(ReservationError::ReservationBelowMailboxThreshold) - .into() - )) - ); - - assert_eq!( - ReservationId::reserve(50_000, 0), - Err(Error::Core( - ExtError::Reservation(ReservationError::ZeroReservationDuration).into() - )) - ); - - assert_eq!( - ReservationId::reserve(mailbox_threshold - 1, 1), - Err(Error::Core( - ExtError::Reservation(ReservationError::ReservationBelowMailboxThreshold) - .into() - )) - ); - - assert_eq!( - ReservationId::reserve(mailbox_threshold, u32::MAX), - Err(Error::Core( - ExtError::Execution(ExecutionError::NotEnoughGas).into() - )) - ); - - assert_eq!( - ReservationId::reserve(u64::MAX, 1), - Err(Error::Core( - ExtError::Execution(ExecutionError::NotEnoughGas).into() - )) - ); - } - InitAction::FreshReserveUnreserve => { - let id = ReservationId::reserve(10_000, 10).unwrap(); - gstd::msg::send_from_reservation(id, msg::source(), b"fresh_reserve_unreserve", 0) - .unwrap(); - assert_eq!( - id.unreserve(), - Err(Error::Core( - ExtError::Reservation(ReservationError::InvalidReservationId).into() - )) - ); - } - } - } - - #[no_mangle] - extern "C" fn handle() { - let action: HandleAction = msg::load().unwrap(); - match action { - HandleAction::Unreserve => { - let id = unsafe { RESERVATION_ID.take().unwrap() }; - id.unreserve().expect("unreservation across executions"); - } - HandleAction::Exit => { - exec::exit(msg::source()); - } - HandleAction::ReplyFromReservation => { - let id = unsafe { RESERVATION_ID.take().unwrap() }; - msg::reply_from_reservation(id, REPLY_FROM_RESERVATION_PAYLOAD, 0) - .expect("unable to reply from reservation"); - } - HandleAction::AddReservationToList(amount, block_count) => { - let reservation_id = - ReservationId::reserve(amount, block_count).expect("Unable to reserve gas"); - unsafe { - RESERVATIONS.push(reservation_id); - } - } - HandleAction::ConsumeReservationsFromList => { - let reservations = unsafe { mem::take(&mut RESERVATIONS) }; - for reservation_id in reservations { - msg::send_from_reservation( - reservation_id, - exec::program_id(), - HandleAction::RunInifitely, - 0, - ) - .expect("Unable to send using reservation"); - } - } - HandleAction::RunInifitely => { - if msg::source() != exec::program_id() { - panic!( - "Invalid caller, this is a private method reserved for the program itself." - ); - } - loop { - let _msg_source = msg::source(); - } - } - HandleAction::SendFromReservationAndUnreserve => { - let id = unsafe { RESERVATION_ID.take().unwrap() }; - gstd::msg::send_from_reservation( - id, - msg::source(), - b"existing_reserve_unreserve", - 0, - ) - .unwrap(); - assert_eq!( - id.unreserve(), - Err(Error::Core( - ExtError::Reservation(ReservationError::InvalidReservationId).into() - )) - ); - } - } - } - - // must be called after `InitAction::Wait` - #[no_mangle] - extern "C" fn handle_reply() { - let action: ReplyAction = msg::load().unwrap(); - unsafe { - WAKE_STATE = match action { - ReplyAction::Panic => WakeState::Panic, - ReplyAction::Exit => WakeState::Exit, - }; - exec::wake(INIT_MSG).unwrap(); - } - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/reserve-gas/src/wasm.rs b/examples/reserve-gas/src/wasm.rs new file mode 100644 index 00000000000..09e1d4ab2d9 --- /dev/null +++ b/examples/reserve-gas/src/wasm.rs @@ -0,0 +1,199 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + HandleAction, InitAction, ReplyAction, REPLY_FROM_RESERVATION_PAYLOAD, RESERVATION_AMOUNT, +}; +use gstd::{ + errors::{Error, ExecutionError, ExtError, ReservationError}, + exec, msg, + prelude::*, + MessageId, ReservationId, +}; + +static mut RESERVATION_ID: Option = None; +static mut RESERVATIONS: Vec = Vec::new(); +static mut INIT_MSG: MessageId = MessageId::new([0; 32]); +static mut WAKE_STATE: WakeState = WakeState::Initial; + +#[derive(Debug, Eq, PartialEq)] +enum WakeState { + Initial, + Panic, + Exit, +} + +#[no_mangle] +extern "C" fn init() { + unsafe { INIT_MSG = msg::id() }; + + let action: InitAction = msg::load().unwrap(); + + match action { + InitAction::Normal(ref reservations) => { + for (amount, duration) in reservations { + let _ = ReservationId::reserve(*amount, *duration).expect("reservation"); + } + + // no actual reservation and unreservation is occurred + let noop_reservation = ReservationId::reserve(50_000, 10).expect("noop reservation"); + let unreserved_amount = noop_reservation.unreserve().expect("noop unreservation"); + assert_eq!(unreserved_amount, 50_000); + + unsafe { + RESERVATION_ID = Some( + ReservationId::reserve(RESERVATION_AMOUNT, 15) + .expect("reservation across executions"), + ) + }; + } + InitAction::Wait => match unsafe { &WAKE_STATE } { + WakeState::Initial => { + let _reservation = ReservationId::reserve(50_000, 10); + // to find message to reply to in test + msg::send(msg::source(), (), 0).unwrap(); + exec::wait(); + } + WakeState::Panic => { + panic!() + } + WakeState::Exit => { + exec::exit(msg::source()); + } + }, + InitAction::CheckArgs { mailbox_threshold } => { + assert_eq!( + ReservationId::reserve(0, 10), + Err(Error::Core( + ExtError::Reservation(ReservationError::ReservationBelowMailboxThreshold) + .into() + )) + ); + + assert_eq!( + ReservationId::reserve(50_000, 0), + Err(Error::Core( + ExtError::Reservation(ReservationError::ZeroReservationDuration).into() + )) + ); + + assert_eq!( + ReservationId::reserve(mailbox_threshold - 1, 1), + Err(Error::Core( + ExtError::Reservation(ReservationError::ReservationBelowMailboxThreshold) + .into() + )) + ); + + assert_eq!( + ReservationId::reserve(mailbox_threshold, u32::MAX), + Err(Error::Core( + ExtError::Execution(ExecutionError::NotEnoughGas).into() + )) + ); + + assert_eq!( + ReservationId::reserve(u64::MAX, 1), + Err(Error::Core( + ExtError::Execution(ExecutionError::NotEnoughGas).into() + )) + ); + } + InitAction::FreshReserveUnreserve => { + let id = ReservationId::reserve(10_000, 10).unwrap(); + gstd::msg::send_from_reservation(id, msg::source(), b"fresh_reserve_unreserve", 0) + .unwrap(); + assert_eq!( + id.unreserve(), + Err(Error::Core( + ExtError::Reservation(ReservationError::InvalidReservationId).into() + )) + ); + } + } +} + +#[no_mangle] +extern "C" fn handle() { + let action: HandleAction = msg::load().unwrap(); + match action { + HandleAction::Unreserve => { + let id = unsafe { RESERVATION_ID.take().unwrap() }; + id.unreserve().expect("unreservation across executions"); + } + HandleAction::Exit => { + exec::exit(msg::source()); + } + HandleAction::ReplyFromReservation => { + let id = unsafe { RESERVATION_ID.take().unwrap() }; + msg::reply_from_reservation(id, REPLY_FROM_RESERVATION_PAYLOAD, 0) + .expect("unable to reply from reservation"); + } + HandleAction::AddReservationToList(amount, block_count) => { + let reservation_id = + ReservationId::reserve(amount, block_count).expect("Unable to reserve gas"); + unsafe { + RESERVATIONS.push(reservation_id); + } + } + HandleAction::ConsumeReservationsFromList => { + let reservations = unsafe { mem::take(&mut RESERVATIONS) }; + for reservation_id in reservations { + msg::send_from_reservation( + reservation_id, + exec::program_id(), + HandleAction::RunInifitely, + 0, + ) + .expect("Unable to send using reservation"); + } + } + HandleAction::RunInifitely => { + if msg::source() != exec::program_id() { + panic!("Invalid caller, this is a private method reserved for the program itself."); + } + loop { + let _msg_source = msg::source(); + } + } + HandleAction::SendFromReservationAndUnreserve => { + let id = unsafe { RESERVATION_ID.take().unwrap() }; + gstd::msg::send_from_reservation(id, msg::source(), b"existing_reserve_unreserve", 0) + .unwrap(); + assert_eq!( + id.unreserve(), + Err(Error::Core( + ExtError::Reservation(ReservationError::InvalidReservationId).into() + )) + ); + } + } +} + +// must be called after `InitAction::Wait` +#[no_mangle] +extern "C" fn handle_reply() { + let action: ReplyAction = msg::load().unwrap(); + unsafe { + WAKE_STATE = match action { + ReplyAction::Panic => WakeState::Panic, + ReplyAction::Exit => WakeState::Exit, + }; + exec::wake(INIT_MSG).unwrap(); + } +} diff --git a/examples/rwlock/Cargo.toml b/examples/rwlock/Cargo.toml index 2d9f2e5bf74..b7a8621cb12 100644 --- a/examples/rwlock/Cargo.toml +++ b/examples/rwlock/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd = { workspace = true, features = ["debug"] } -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/rwlock/src/lib.rs b/examples/rwlock/src/lib.rs index 23e073c2fea..8011edf5d94 100644 --- a/examples/rwlock/src/lib.rs +++ b/examples/rwlock/src/lib.rs @@ -39,111 +39,4 @@ pub enum Command { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::Command; - use core::{ - future::Future, - pin::Pin, - ptr, - task::{Context, RawWaker, RawWakerVTable, Waker}, - }; - use gstd::{msg, prelude::*, sync::RwLock, ActorId}; - - static mut DESTINATION: ActorId = ActorId::zero(); - static RW_LOCK: RwLock = RwLock::new(0); - - async fn ping() { - msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) - .expect("Failed to send message") - .await - .expect("Received error reply"); - } - - #[no_mangle] - extern "C" fn init() { - let destination = msg::load().expect("Failed to load destination"); - unsafe { DESTINATION = destination }; - } - - #[gstd::async_main] - async fn main() { - if let Ok(command) = msg::load() { - match command { - Command::Get => { - let value = RW_LOCK.read().await; - msg::reply(*value, 0).expect("Failed to send reply"); - } - Command::Inc => { - let mut value = RW_LOCK.write().await; - *value += 1; - } - Command::PingGet => { - ping().await; - let value = RW_LOCK.read().await; - msg::reply(*value, 0).expect("Failed to send reply"); - } - Command::IncPing => { - let mut value = RW_LOCK.write().await; - *value += 1; - ping().await; - } - Command::GetPing => { - let value = RW_LOCK.read().await; - ping().await; - msg::reply(*value, 0).expect("Failed to send reply"); - } - Command::CheckReaders => { - let mut storage = Vec::with_capacity(RwLock::::READERS_LIMIT as usize); - - for _ in 0..RwLock::::READERS_LIMIT { - storage.push(RW_LOCK.read().await); - } - - let waker = unsafe { Waker::from_raw(clone_waker(ptr::null())) }; - let mut cx = Context::from_waker(&waker); - - // Read future just for extra testing - let mut wf = RW_LOCK.write(); - - assert!( - !Pin::new(&mut wf).poll(&mut cx).is_ready(), - "Ready, but shouldn't" - ); - - let mut rf = RW_LOCK.read(); - - assert!( - !Pin::new(&mut rf).poll(&mut cx).is_ready(), - "Ready, but shouldn't" - ); - - // Drop of single reader. - storage.pop(); - - // Read future just for extra testing - assert!( - !Pin::new(&mut wf).poll(&mut cx).is_ready(), - "Ready, but shouldn't" - ); - assert!( - Pin::new(&mut rf).poll(&mut cx).is_ready(), - "Not ready, but shouldn't" - ); - - let value = rf.await; - msg::reply(*value, 0).expect("Failed to send reply"); - } - } - } else { - let _write = RW_LOCK.write().await; - RW_LOCK.read().await; - } - } - - unsafe fn clone_waker(ptr: *const ()) -> RawWaker { - RawWaker::new( - ptr, - &RawWakerVTable::new(clone_waker, |_| {}, |_| {}, |_| {}), - ) - } -} +mod wasm; diff --git a/examples/rwlock/src/wasm.rs b/examples/rwlock/src/wasm.rs new file mode 100644 index 00000000000..813f733a4b1 --- /dev/null +++ b/examples/rwlock/src/wasm.rs @@ -0,0 +1,124 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Command; +use core::{ + future::Future, + pin::Pin, + ptr, + task::{Context, RawWaker, RawWakerVTable, Waker}, +}; +use gstd::{msg, prelude::*, sync::RwLock, ActorId}; + +static mut DESTINATION: ActorId = ActorId::zero(); +static RW_LOCK: RwLock = RwLock::new(0); + +async fn ping() { + msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) + .expect("Failed to send message") + .await + .expect("Received error reply"); +} + +#[no_mangle] +extern "C" fn init() { + let destination = msg::load().expect("Failed to load destination"); + unsafe { DESTINATION = destination }; +} + +#[gstd::async_main] +async fn main() { + if let Ok(command) = msg::load() { + match command { + Command::Get => { + let value = RW_LOCK.read().await; + msg::reply(*value, 0).expect("Failed to send reply"); + } + Command::Inc => { + let mut value = RW_LOCK.write().await; + *value += 1; + } + Command::PingGet => { + ping().await; + let value = RW_LOCK.read().await; + msg::reply(*value, 0).expect("Failed to send reply"); + } + Command::IncPing => { + let mut value = RW_LOCK.write().await; + *value += 1; + ping().await; + } + Command::GetPing => { + let value = RW_LOCK.read().await; + ping().await; + msg::reply(*value, 0).expect("Failed to send reply"); + } + Command::CheckReaders => { + let mut storage = Vec::with_capacity(RwLock::::READERS_LIMIT as usize); + + for _ in 0..RwLock::::READERS_LIMIT { + storage.push(RW_LOCK.read().await); + } + + let waker = unsafe { Waker::from_raw(clone_waker(ptr::null())) }; + let mut cx = Context::from_waker(&waker); + + // Read future just for extra testing + let mut wf = RW_LOCK.write(); + + assert!( + !Pin::new(&mut wf).poll(&mut cx).is_ready(), + "Ready, but shouldn't" + ); + + let mut rf = RW_LOCK.read(); + + assert!( + !Pin::new(&mut rf).poll(&mut cx).is_ready(), + "Ready, but shouldn't" + ); + + // Drop of single reader. + storage.pop(); + + // Read future just for extra testing + assert!( + !Pin::new(&mut wf).poll(&mut cx).is_ready(), + "Ready, but shouldn't" + ); + assert!( + Pin::new(&mut rf).poll(&mut cx).is_ready(), + "Not ready, but shouldn't" + ); + + let value = rf.await; + msg::reply(*value, 0).expect("Failed to send reply"); + } + } + } else { + let _write = RW_LOCK.write().await; + RW_LOCK.read().await; + } +} + +unsafe fn clone_waker(ptr: *const ()) -> RawWaker { + RawWaker::new( + ptr, + &RawWakerVTable::new(clone_waker, |_| {}, |_| {}, |_| {}), + ) +} diff --git a/examples/send-from-reservation/Cargo.toml b/examples/send-from-reservation/Cargo.toml index 0cfee5b00e8..cbde057d74b 100644 --- a/examples/send-from-reservation/Cargo.toml +++ b/examples/send-from-reservation/Cargo.toml @@ -2,13 +2,14 @@ name = "demo-send-from-reservation" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -16,8 +17,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/send-from-reservation/src/lib.rs b/examples/send-from-reservation/src/lib.rs index aa927e0333e..7348f9fbb6d 100644 --- a/examples/send-from-reservation/src/lib.rs +++ b/examples/send-from-reservation/src/lib.rs @@ -42,84 +42,4 @@ pub enum HandleAction { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gstd::{msg, prelude::*, ReservationId}; - - #[derive(Debug, Encode, Decode)] - pub struct Receive([u8; 32]); - - #[no_mangle] - extern "C" fn handle() { - let action: HandleAction = msg::load().expect("Failed to load handle payload"); - match action { - HandleAction::SendToUser => { - let id = ReservationId::reserve(3_000_000_000, 50).expect("Failed to reserve gas"); - msg::send_bytes_from_reservation(id, msg::source(), b"send_to_user", 500) - .expect("Failed to send message"); - } - HandleAction::SendToUserDelayed => { - let id = ReservationId::reserve(4_000_000_000, 60).expect("Failed to reserve gas"); - msg::send_bytes_delayed_from_reservation( - id, - msg::source(), - b"send_to_user_delayed", - 600, - 1, - ) - .expect("Failed to send message"); - } - HandleAction::SendToProgram { pid, user } => { - let id = ReservationId::reserve(5_000_000_000, 70).expect("Failed to reserve gas"); - msg::send_from_reservation( - id, - pid.into(), - HandleAction::ReceiveFromProgram(user), - 700, - ) - .expect("Failed to send message"); - } - HandleAction::SendToProgramDelayed { pid, user } => { - let id = ReservationId::reserve(6_000_000_000, 80).expect("Failed to reserve gas"); - msg::send_delayed_from_reservation( - id, - pid.into(), - HandleAction::ReceiveFromProgramDelayed(user), - 800, - 1, - ) - .expect("Failed to send message"); - } - HandleAction::ReplyToUser => { - let id = ReservationId::reserve(7_000_000_000, 90).expect("Failed to reserve gas"); - msg::reply_bytes_from_reservation(id, b"reply_to_user", 900) - .expect("Failed to send message"); - } - HandleAction::ReplyToProgram { pid, user } => { - msg::send(pid.into(), HandleAction::ReplyToProgramStep2(user), 900) - .expect("Failed to reserve gas"); - } - HandleAction::ReplyToProgramStep2(user) => { - let id = ReservationId::reserve(7_000_000_000, 90).expect("Failed to reserve gas"); - msg::reply_from_reservation(id, Receive(user), 900).expect("Failed to reply"); - } - HandleAction::ReceiveFromProgram(user) => { - assert_eq!(msg::value(), 700); - msg::send_bytes(user.into(), b"receive_from_program", 700) - .expect("Failed to send message"); - } - HandleAction::ReceiveFromProgramDelayed(user) => { - assert_eq!(msg::value(), 800); - msg::send_bytes(user.into(), b"receive_from_program_delayed", 800) - .expect("Failed to send message"); - } - } - } - - #[no_mangle] - extern "C" fn handle_reply() { - let Receive(user) = msg::load().expect("Failed to load handle payload"); - assert_eq!(msg::value(), 900); - msg::send_bytes(user.into(), b"reply", 900).expect("Failed to send message"); - } -} +mod wasm; diff --git a/examples/send-from-reservation/src/wasm.rs b/examples/send-from-reservation/src/wasm.rs new file mode 100644 index 00000000000..a8281f605ec --- /dev/null +++ b/examples/send-from-reservation/src/wasm.rs @@ -0,0 +1,92 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::HandleAction; +use gstd::{msg, prelude::*, ReservationId}; + +#[derive(Debug, Encode, Decode)] +pub struct Receive([u8; 32]); + +#[no_mangle] +extern "C" fn handle() { + let action: HandleAction = msg::load().expect("Failed to load handle payload"); + match action { + HandleAction::SendToUser => { + let id = ReservationId::reserve(3_000_000_000, 50).expect("Failed to reserve gas"); + msg::send_bytes_from_reservation(id, msg::source(), b"send_to_user", 500) + .expect("Failed to send message"); + } + HandleAction::SendToUserDelayed => { + let id = ReservationId::reserve(4_000_000_000, 60).expect("Failed to reserve gas"); + msg::send_bytes_delayed_from_reservation( + id, + msg::source(), + b"send_to_user_delayed", + 600, + 1, + ) + .expect("Failed to send message"); + } + HandleAction::SendToProgram { pid, user } => { + let id = ReservationId::reserve(5_000_000_000, 70).expect("Failed to reserve gas"); + msg::send_from_reservation(id, pid.into(), HandleAction::ReceiveFromProgram(user), 700) + .expect("Failed to send message"); + } + HandleAction::SendToProgramDelayed { pid, user } => { + let id = ReservationId::reserve(6_000_000_000, 80).expect("Failed to reserve gas"); + msg::send_delayed_from_reservation( + id, + pid.into(), + HandleAction::ReceiveFromProgramDelayed(user), + 800, + 1, + ) + .expect("Failed to send message"); + } + HandleAction::ReplyToUser => { + let id = ReservationId::reserve(7_000_000_000, 90).expect("Failed to reserve gas"); + msg::reply_bytes_from_reservation(id, b"reply_to_user", 900) + .expect("Failed to send message"); + } + HandleAction::ReplyToProgram { pid, user } => { + msg::send(pid.into(), HandleAction::ReplyToProgramStep2(user), 900) + .expect("Failed to reserve gas"); + } + HandleAction::ReplyToProgramStep2(user) => { + let id = ReservationId::reserve(7_000_000_000, 90).expect("Failed to reserve gas"); + msg::reply_from_reservation(id, Receive(user), 900).expect("Failed to reply"); + } + HandleAction::ReceiveFromProgram(user) => { + assert_eq!(msg::value(), 700); + msg::send_bytes(user.into(), b"receive_from_program", 700) + .expect("Failed to send message"); + } + HandleAction::ReceiveFromProgramDelayed(user) => { + assert_eq!(msg::value(), 800); + msg::send_bytes(user.into(), b"receive_from_program_delayed", 800) + .expect("Failed to send message"); + } + } +} + +#[no_mangle] +extern "C" fn handle_reply() { + let Receive(user) = msg::load().expect("Failed to load handle payload"); + assert_eq!(msg::value(), 900); + msg::send_bytes(user.into(), b"reply", 900).expect("Failed to send message"); +} diff --git a/examples/signal-entry/Cargo.toml b/examples/signal-entry/Cargo.toml index 4dca39ea3ef..7826e3d3394 100644 --- a/examples/signal-entry/Cargo.toml +++ b/examples/signal-entry/Cargo.toml @@ -2,17 +2,17 @@ name = "demo-signal-entry" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd = { workspace = true, features = [ "debug", ] } # debug is used here, because `signal_backend_error_invalid_debug_works` test in `pallet-gear` requires it to be working correctly in release mode -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true gear-core.workspace = true -gsys.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -20,8 +20,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/signal-entry/src/lib.rs b/examples/signal-entry/src/lib.rs index dcf9c054f97..7d6a1de52de 100644 --- a/examples/signal-entry/src/lib.rs +++ b/examples/signal-entry/src/lib.rs @@ -59,264 +59,7 @@ pub enum HandleAction { pub const WAIT_AND_RESERVE_WITH_PANIC_GAS: u64 = 10_000_000_000; #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gear_core::ids::ProgramId; - use gstd::{ - debug, - errors::{ExtError, ReservationError, SignalCode, SimpleExecutionError}, - exec, - ext::oom_panic, - msg, - prelude::*, - ActorId, MessageId, - }; - - static mut INITIATOR: ActorId = ActorId::zero(); - static mut HANDLE_MSG: Option = None; - static mut DO_PANIC: bool = false; - static mut DO_EXIT: bool = false; - static mut HANDLE_SIGNAL_STATE: HandleSignalState = HandleSignalState::Normal; - - enum HandleSignalState { - Normal, - Panic, - ForbiddenCall([u8; 32]), - Assert(SignalCode), - } - - #[no_mangle] - extern "C" fn init() { - unsafe { INITIATOR = msg::source() }; - } - - #[no_mangle] - extern "C" fn handle() { - unsafe { HANDLE_MSG = Some(msg::id()) }; - let do_panic = unsafe { &mut DO_PANIC }; - - let action: HandleAction = msg::load().unwrap(); - match action { - HandleAction::Simple => { - // must be unreserved as unused - exec::system_reserve_gas(100).unwrap(); - } - HandleAction::Wait => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::WaitAndPanic => { - if *do_panic { - panic!(); - } - - *do_panic = !*do_panic; - - exec::system_reserve_gas(200).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::WaitAndReserveWithPanic => { - if *do_panic { - exec::system_reserve_gas(1_000_000_000).unwrap(); - panic!(); - } - - *do_panic = !*do_panic; - - exec::system_reserve_gas(WAIT_AND_RESERVE_WITH_PANIC_GAS).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::WaitAndExit => { - if unsafe { DO_EXIT } { - msg::send_bytes(msg::source(), b"wait_and_exit", 0).unwrap(); - exec::exit(msg::source()); - } - - unsafe { DO_EXIT = !DO_EXIT }; - - exec::system_reserve_gas(900).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::Panic => { - exec::system_reserve_gas(5_000_000_000).unwrap(); - panic!(); - } - HandleAction::WaitWithReserveAmountAndPanic(gas_amount) => { - if *do_panic { - panic!(); - } - - *do_panic = !*do_panic; - - exec::system_reserve_gas(gas_amount).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::Exit => { - exec::system_reserve_gas(4_000_000_000).unwrap(); - msg::reply_bytes(b"exit", 0).unwrap(); - exec::exit(msg::source()); - } - HandleAction::Accumulate => { - exec::system_reserve_gas(1000).unwrap(); - exec::system_reserve_gas(234).unwrap(); - exec::wait(); - } - HandleAction::OutOfGas => { - exec::system_reserve_gas(5_000_000_000).unwrap(); - // used to found message id in test - msg::send(msg::source(), 0, 0).unwrap(); - #[allow(clippy::empty_loop)] - loop {} - } - HandleAction::AcrossWaits => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - // used to found message id in test - // we use send instead of reply to avoid duplicated reply error. - msg::send(msg::source(), 0, 0).unwrap(); - exec::wait(); - } - HandleAction::PanicInSignal => { - unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::Panic }; - exec::system_reserve_gas(5_000_000_000).unwrap(); - exec::wait(); - } - HandleAction::ZeroReserve => { - let res = exec::system_reserve_gas(0); - assert_eq!( - res, - Err(ExtError::Reservation(ReservationError::ZeroReservationAmount).into()) - ); - } - HandleAction::ForbiddenCallInSignal(user) => { - unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::ForbiddenCall(user) }; - exec::system_reserve_gas(1_000_000_000).unwrap(); - exec::wait(); - } - HandleAction::ForbiddenAction => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - msg::send(ActorId::new(ProgramId::SYSTEM.into()), "hello", 0) - .expect("cannot send message"); - } - HandleAction::SaveSignal(signal_received) => { - debug!("handle: signal_received={:?}", signal_received); - - unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::Assert(signal_received) }; - } - HandleAction::ExceedMemory => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - oom_panic(); - } - HandleAction::UnreachableInstruction => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - #[cfg(target_arch = "wasm32")] - core::arch::wasm32::unreachable(); - } - HandleAction::InvalidDebugCall => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - #[allow(clippy::invalid_utf8_in_unchecked)] - let invalid_string = unsafe { core::str::from_utf8_unchecked(&[0, 159, 146, 150]) }; - debug!("{}", invalid_string); - } - HandleAction::UnrecoverableExt => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - exec::wait_up_to(0); - } - HandleAction::IncorrectFree => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - extern "C" { - fn free(ptr: *mut u8) -> *mut u8; - } - - unsafe { - free(usize::MAX as *mut u8); - } - } - HandleAction::WaitWithoutSendingMessage => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - exec::wait(); - } - HandleAction::MemoryAccess => { - exec::system_reserve_gas(1_000_000_000).unwrap(); - - const ARRAY_SIZE: usize = 1_000_000; - let arr = [42u8; ARRAY_SIZE]; - - #[allow(clippy::needless_range_loop)] - for i in 0..ARRAY_SIZE { - let _value = arr[i]; - } - } - } - } - - #[no_mangle] - extern "C" fn handle_signal() { - match unsafe { &HANDLE_SIGNAL_STATE } { - HandleSignalState::Normal => { - msg::send(unsafe { INITIATOR }, b"handle_signal", 0).unwrap(); - let signal_code = msg::signal_code() - .expect("Incorrect call") - .expect("Unsupported code"); - assert!(matches!( - signal_code, - SignalCode::Execution( - SimpleExecutionError::UserspacePanic | SimpleExecutionError::RanOutOfGas - ) - )); - - if let Some(handle_msg) = unsafe { HANDLE_MSG } { - assert_eq!(msg::signal_from(), Ok(handle_msg)); - } - - // TODO: check gas limit (#1796) - // assert_eq!(msg::gas_limit(), 5_000_000_000); - } - HandleSignalState::Panic => { - // to be sure state rolls back so this message won't appear in mailbox in test - msg::send(unsafe { INITIATOR }, b"handle_signal_panic", 0).unwrap(); - panic!(); - } - HandleSignalState::ForbiddenCall(user) => { - msg::send_bytes((*user).into(), b"handle_signal_forbidden_call", 0).unwrap(); - let _ = msg::source(); - } - HandleSignalState::Assert(signal_saved) => { - let signal_received = msg::signal_code() - .expect("Incorrect call") - .expect("Unsupported code"); - - if signal_received == *signal_saved { - msg::send(unsafe { INITIATOR }, true, 0).unwrap(); - } else { - msg::send(unsafe { INITIATOR }, false, 0).unwrap(); - } - } - } - } - - #[no_mangle] - extern "C" fn handle_reply() { - let handle_msg = unsafe { HANDLE_MSG.unwrap() }; - exec::wake(handle_msg).unwrap(); - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/signal-entry/src/wasm.rs b/examples/signal-entry/src/wasm.rs new file mode 100644 index 00000000000..b483538a356 --- /dev/null +++ b/examples/signal-entry/src/wasm.rs @@ -0,0 +1,274 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{HandleAction, WAIT_AND_RESERVE_WITH_PANIC_GAS}; +use gear_core::ids::ProgramId; +use gstd::{ + debug, + errors::{ExtError, ReservationError, SignalCode, SimpleExecutionError}, + exec, + ext::oom_panic, + msg, + prelude::*, + ActorId, MessageId, +}; + +static mut INITIATOR: ActorId = ActorId::zero(); +static mut HANDLE_MSG: Option = None; +static mut DO_PANIC: bool = false; +static mut DO_EXIT: bool = false; +static mut HANDLE_SIGNAL_STATE: HandleSignalState = HandleSignalState::Normal; + +enum HandleSignalState { + Normal, + Panic, + ForbiddenCall([u8; 32]), + Assert(SignalCode), +} + +#[no_mangle] +extern "C" fn init() { + unsafe { INITIATOR = msg::source() }; +} + +#[no_mangle] +extern "C" fn handle() { + unsafe { HANDLE_MSG = Some(msg::id()) }; + let do_panic = unsafe { &mut DO_PANIC }; + + let action: HandleAction = msg::load().unwrap(); + match action { + HandleAction::Simple => { + // must be unreserved as unused + exec::system_reserve_gas(100).unwrap(); + } + HandleAction::Wait => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::WaitAndPanic => { + if *do_panic { + panic!(); + } + + *do_panic = !*do_panic; + + exec::system_reserve_gas(200).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::WaitAndReserveWithPanic => { + if *do_panic { + exec::system_reserve_gas(1_000_000_000).unwrap(); + panic!(); + } + + *do_panic = !*do_panic; + + exec::system_reserve_gas(WAIT_AND_RESERVE_WITH_PANIC_GAS).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::WaitAndExit => { + if unsafe { DO_EXIT } { + msg::send_bytes(msg::source(), b"wait_and_exit", 0).unwrap(); + exec::exit(msg::source()); + } + + unsafe { DO_EXIT = !DO_EXIT }; + + exec::system_reserve_gas(900).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::Panic => { + exec::system_reserve_gas(5_000_000_000).unwrap(); + panic!(); + } + HandleAction::WaitWithReserveAmountAndPanic(gas_amount) => { + if *do_panic { + panic!(); + } + + *do_panic = !*do_panic; + + exec::system_reserve_gas(gas_amount).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::Exit => { + exec::system_reserve_gas(4_000_000_000).unwrap(); + msg::reply_bytes(b"exit", 0).unwrap(); + exec::exit(msg::source()); + } + HandleAction::Accumulate => { + exec::system_reserve_gas(1000).unwrap(); + exec::system_reserve_gas(234).unwrap(); + exec::wait(); + } + HandleAction::OutOfGas => { + exec::system_reserve_gas(5_000_000_000).unwrap(); + // used to found message id in test + msg::send(msg::source(), 0, 0).unwrap(); + #[allow(clippy::empty_loop)] + loop {} + } + HandleAction::AcrossWaits => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + // used to found message id in test + // we use send instead of reply to avoid duplicated reply error. + msg::send(msg::source(), 0, 0).unwrap(); + exec::wait(); + } + HandleAction::PanicInSignal => { + unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::Panic }; + exec::system_reserve_gas(5_000_000_000).unwrap(); + exec::wait(); + } + HandleAction::ZeroReserve => { + let res = exec::system_reserve_gas(0); + assert_eq!( + res, + Err(ExtError::Reservation(ReservationError::ZeroReservationAmount).into()) + ); + } + HandleAction::ForbiddenCallInSignal(user) => { + unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::ForbiddenCall(user) }; + exec::system_reserve_gas(1_000_000_000).unwrap(); + exec::wait(); + } + HandleAction::ForbiddenAction => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + msg::send(ActorId::new(ProgramId::SYSTEM.into()), "hello", 0) + .expect("cannot send message"); + } + HandleAction::SaveSignal(signal_received) => { + debug!("handle: signal_received={:?}", signal_received); + + unsafe { HANDLE_SIGNAL_STATE = HandleSignalState::Assert(signal_received) }; + } + HandleAction::ExceedMemory => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + oom_panic(); + } + HandleAction::UnreachableInstruction => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + } + HandleAction::InvalidDebugCall => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + #[allow(clippy::invalid_utf8_in_unchecked)] + let invalid_string = unsafe { core::str::from_utf8_unchecked(&[0, 159, 146, 150]) }; + debug!("{}", invalid_string); + } + HandleAction::UnrecoverableExt => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + exec::wait_up_to(0); + } + HandleAction::IncorrectFree => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + extern "C" { + fn free(ptr: *mut u8) -> *mut u8; + } + + unsafe { + free(usize::MAX as *mut u8); + } + } + HandleAction::WaitWithoutSendingMessage => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + exec::wait(); + } + HandleAction::MemoryAccess => { + exec::system_reserve_gas(1_000_000_000).unwrap(); + + const ARRAY_SIZE: usize = 1_000_000; + let arr = [42u8; ARRAY_SIZE]; + + #[allow(clippy::needless_range_loop)] + for i in 0..ARRAY_SIZE { + let _value = arr[i]; + } + } + } +} + +#[no_mangle] +extern "C" fn handle_signal() { + match unsafe { &HANDLE_SIGNAL_STATE } { + HandleSignalState::Normal => { + msg::send(unsafe { INITIATOR }, b"handle_signal", 0).unwrap(); + let signal_code = msg::signal_code() + .expect("Incorrect call") + .expect("Unsupported code"); + assert!(matches!( + signal_code, + SignalCode::Execution( + SimpleExecutionError::UserspacePanic | SimpleExecutionError::RanOutOfGas + ) + )); + + if let Some(handle_msg) = unsafe { HANDLE_MSG } { + assert_eq!(msg::signal_from(), Ok(handle_msg)); + } + + // TODO: check gas limit (#1796) + // assert_eq!(msg::gas_limit(), 5_000_000_000); + } + HandleSignalState::Panic => { + // to be sure state rolls back so this message won't appear in mailbox in test + msg::send(unsafe { INITIATOR }, b"handle_signal_panic", 0).unwrap(); + panic!(); + } + HandleSignalState::ForbiddenCall(user) => { + msg::send_bytes((*user).into(), b"handle_signal_forbidden_call", 0).unwrap(); + let _ = msg::source(); + } + HandleSignalState::Assert(signal_saved) => { + let signal_received = msg::signal_code() + .expect("Incorrect call") + .expect("Unsupported code"); + + if signal_received == *signal_saved { + msg::send(unsafe { INITIATOR }, true, 0).unwrap(); + } else { + msg::send(unsafe { INITIATOR }, false, 0).unwrap(); + } + } + } +} + +#[no_mangle] +extern "C" fn handle_reply() { + let handle_msg = unsafe { HANDLE_MSG.unwrap() }; + exec::wake(handle_msg).unwrap(); +} diff --git a/examples/stack-allocations/Cargo.toml b/examples/stack-allocations/Cargo.toml index 5cf923ff1e8..e6b8fe0c32e 100644 --- a/examples/stack-allocations/Cargo.toml +++ b/examples/stack-allocations/Cargo.toml @@ -4,16 +4,17 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [dev-dependencies] gtest.workspace = true -rand_pcg = { workspace = true } -rand = "0.8" +rand_pcg.workspace = true +rand.workspace = true static_assertions.workspace = true [build-dependencies] diff --git a/examples/stack-allocations/src/lib.rs b/examples/stack-allocations/src/lib.rs index 43440bf2ba4..5aa80e5d0aa 100644 --- a/examples/stack-allocations/src/lib.rs +++ b/examples/stack-allocations/src/lib.rs @@ -59,47 +59,7 @@ pub struct ReplyData { } #[cfg(not(feature = "std"))] -mod wasm { - use crate::{Action, HandleData, ReplyData, Vec}; - use gstd::msg; - - struct State { - actions: Vec, - } - - static mut STATE: State = State { - actions: Vec::new(), - }; - - fn do_actions(mut actions: Vec) -> u32 { - let check_sum = match actions.pop() { - Some(Action::Read) => msg::with_read_on_stack(|payload| { - payload - .map(|payload| payload.iter().fold(0u32, |acc, x| acc + *x as u32)) - .expect("Failed to read payload") - }), - Some(Action::Load) => { - let HandleData { data } = msg::load().expect("Failed to load handle config"); - data.iter().fold(0, |acc, x| acc + *x as u32) - } - None => return 0, - }; - check_sum + do_actions(actions) - } - - #[no_mangle] - extern "C" fn handle() { - let check_sum = do_actions(unsafe { STATE.actions.clone() }); - msg::reply(ReplyData { check_sum }, 0).expect("Failed to reply"); - } - - #[no_mangle] - extern "C" fn init() { - unsafe { - STATE.actions = msg::load().expect("Failed to load init config"); - } - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/stack-allocations/src/wasm.rs b/examples/stack-allocations/src/wasm.rs new file mode 100644 index 00000000000..2f7876db301 --- /dev/null +++ b/examples/stack-allocations/src/wasm.rs @@ -0,0 +1,62 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! This contract recursively calls payload stack allocated read or load, +//! depends on actions vector, which is set in init. +//! For each recursion step we call check_sum, which is sum of all payload bytes. +//! Then reply summary check_sum back to source account. + +use crate::{Action, HandleData, ReplyData, Vec}; +use gstd::msg; + +struct State { + actions: Vec, +} + +static mut STATE: State = State { + actions: Vec::new(), +}; + +fn do_actions(mut actions: Vec) -> u32 { + let check_sum = match actions.pop() { + Some(Action::Read) => msg::with_read_on_stack(|payload| { + payload + .map(|payload| payload.iter().fold(0u32, |acc, x| acc + *x as u32)) + .expect("Failed to read payload") + }), + Some(Action::Load) => { + let HandleData { data } = msg::load().expect("Failed to load handle config"); + data.iter().fold(0, |acc, x| acc + *x as u32) + } + None => return 0, + }; + check_sum + do_actions(actions) +} + +#[no_mangle] +extern "C" fn handle() { + let check_sum = do_actions(unsafe { STATE.actions.clone() }); + msg::reply(ReplyData { check_sum }, 0).expect("Failed to reply"); +} + +#[no_mangle] +extern "C" fn init() { + unsafe { + STATE.actions = msg::load().expect("Failed to load init config"); + } +} diff --git a/examples/state-rollback/Cargo.toml b/examples/state-rollback/Cargo.toml index 99d46e405c5..c4d156bc250 100644 --- a/examples/state-rollback/Cargo.toml +++ b/examples/state-rollback/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/state-rollback/src/lib.rs b/examples/state-rollback/src/lib.rs index b430f5f52ca..fbc08d47a0b 100644 --- a/examples/state-rollback/src/lib.rs +++ b/examples/state-rollback/src/lib.rs @@ -27,31 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{exec, msg, prelude::*}; - - static mut PAYLOAD: Option> = None; - - #[no_mangle] - extern "C" fn handle() { - let payload = msg::load_bytes().expect("Failed to load payload"); - - // Previous value - msg::send(msg::source(), unsafe { &PAYLOAD }, 0).expect("Failed to send message"); - - let is_panic = payload == b"panic"; - let is_leave = payload == b"leave"; - - // New value setting - unsafe { PAYLOAD = Some(payload) }; - - // Newly set value - msg::reply(unsafe { &PAYLOAD }, 0).expect("Failed to send reply"); - - // Stop execution with panic. - is_panic.then(|| panic!()); - - // Stop execution with leave. - is_leave.then(|| exec::leave()); - } -} +mod wasm; diff --git a/examples/state-rollback/src/wasm.rs b/examples/state-rollback/src/wasm.rs new file mode 100644 index 00000000000..6499386ef5e --- /dev/null +++ b/examples/state-rollback/src/wasm.rs @@ -0,0 +1,44 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{exec, msg, prelude::*}; + +static mut PAYLOAD: Option> = None; + +#[no_mangle] +extern "C" fn handle() { + let payload = msg::load_bytes().expect("Failed to load payload"); + + // Previous value + msg::send(msg::source(), unsafe { &PAYLOAD }, 0).expect("Failed to send message"); + + let is_panic = payload == b"panic"; + let is_leave = payload == b"leave"; + + // New value setting + unsafe { PAYLOAD = Some(payload) }; + + // Newly set value + msg::reply(unsafe { &PAYLOAD }, 0).expect("Failed to send reply"); + + // Stop execution with panic. + is_panic.then(|| panic!()); + + // Stop execution with leave. + is_leave.then(|| exec::leave()); +} diff --git a/examples/sync-duplicate/Cargo.toml b/examples/sync-duplicate/Cargo.toml index 8f9ae9d0e87..f0d0a3adf96 100644 --- a/examples/sync-duplicate/Cargo.toml +++ b/examples/sync-duplicate/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/sync-duplicate/src/lib.rs b/examples/sync-duplicate/src/lib.rs index 745af7ac4ce..fbc08d47a0b 100644 --- a/examples/sync-duplicate/src/lib.rs +++ b/examples/sync-duplicate/src/lib.rs @@ -27,33 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, prelude::*, ActorId}; - - static mut COUNTER: i32 = 0; - static mut DESTINATION: ActorId = ActorId::zero(); - - #[no_mangle] - extern "C" fn init() { - let destination = msg::load().expect("Failed to load destination"); - unsafe { DESTINATION = destination }; - } - - #[gstd::async_main] - async fn main() { - let payload = msg::load_bytes().expect("Failed to load payload"); - - if payload == b"async" { - unsafe { COUNTER += 1 }; - - let _ = msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) - .expect("Failed to send message") - .await - .expect("Received error reply"); - - msg::reply(unsafe { COUNTER }, 0).expect("Failed to send reply"); - - unsafe { COUNTER = 0 }; - } - } -} +mod wasm; diff --git a/examples/sync-duplicate/src/wasm.rs b/examples/sync-duplicate/src/wasm.rs new file mode 100644 index 00000000000..f949f4afdcd --- /dev/null +++ b/examples/sync-duplicate/src/wasm.rs @@ -0,0 +1,46 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{msg, prelude::*, ActorId}; + +static mut COUNTER: i32 = 0; +static mut DESTINATION: ActorId = ActorId::zero(); + +#[no_mangle] +extern "C" fn init() { + let destination = msg::load().expect("Failed to load destination"); + unsafe { DESTINATION = destination }; +} + +#[gstd::async_main] +async fn main() { + let payload = msg::load_bytes().expect("Failed to load payload"); + + if payload == b"async" { + unsafe { COUNTER += 1 }; + + let _ = msg::send_bytes_for_reply(unsafe { DESTINATION }, "PING", 0, 0) + .expect("Failed to send message") + .await + .expect("Received error reply"); + + msg::reply(unsafe { COUNTER }, 0).expect("Failed to send reply"); + + unsafe { COUNTER = 0 }; + } +} diff --git a/examples/sys-calls/Cargo.toml b/examples/sys-calls/Cargo.toml index ba8908b4021..1e0e64155bd 100644 --- a/examples/sys-calls/Cargo.toml +++ b/examples/sys-calls/Cargo.toml @@ -2,19 +2,18 @@ name = "test-syscalls" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -gstd = { workspace = true } -parity-scale-codec = { workspace = true, features = ["derive"] } +gstd.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] wasm-wrapper = [] diff --git a/examples/sys-calls/src/lib.rs b/examples/sys-calls/src/lib.rs index 48940fc8bbe..ea6d8473761 100644 --- a/examples/sys-calls/src/lib.rs +++ b/examples/sys-calls/src/lib.rs @@ -105,429 +105,4 @@ pub enum Kind { pub const PAY_PROGRAM_RENT_EXPECT: &str = "Unable to pay rent"; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - use super::Kind; - use gstd::{ - errors::{ReplyCode, SignalCode, SimpleExecutionError}, - exec, format, - msg::{self, MessageHandle}, - prog, ActorId, CodeId, MessageId, ReservationId, Vec, - }; - use parity_scale_codec::Encode; - - static mut CODE_ID: CodeId = CodeId::new([0u8; 32]); - static mut SIGNAL_DETAILS: (MessageId, SignalCode, ActorId) = ( - MessageId::new([0; 32]), - SignalCode::Execution(SimpleExecutionError::Unsupported), - ActorId::zero(), - ); - static mut DO_PANIC: bool = false; - - #[no_mangle] - extern "C" fn init() { - let code_id_bytes: [u8; 32] = msg::load().expect("internal error: invalid payload"); - - unsafe { CODE_ID = code_id_bytes.into() }; - } - - #[no_mangle] - extern "C" fn handle() { - let syscall_kinds: Vec = msg::load().expect("internal error: invalid payload"); - for syscall_kind in syscall_kinds { - process(syscall_kind); - } - - // Report test executed successfully - msg::send_delayed(msg::source(), b"ok", 0, 0).expect("internal error: report send failed"); - } - - fn process(syscall_kind: Kind) { - match syscall_kind { - Kind::CreateProgram(salt, gas_opt, (expected_mid, expected_pid)) => { - let salt = salt.to_le_bytes(); - let res = match gas_opt { - Some(gas) => prog::create_program_bytes_with_gas_delayed( - unsafe { CODE_ID }, - salt, - "payload", - gas, - 0, - 0, - ), - None => prog::create_program_bytes_delayed( - unsafe { CODE_ID }, - salt, - "payload", - 0, - 0, - ), - }; - let (actual_mid, actual_pid) = res.expect("internal error: create program failed"); - let actual_mid: [u8; 32] = actual_mid.into(); - let actual_pid: [u8; 32] = actual_pid.into(); - assert_eq!( - expected_mid, actual_mid, - "Kind::CreateProgram: mid test failed" - ); - assert_eq!( - expected_pid, actual_pid, - "Kind::CreateProgram: pid test failed" - ); - } - Kind::Error(message_value, expected_err) => { - let actual_err = msg::reply(b"", message_value).expect_err("not enough balance"); - assert_eq!( - expected_err, - format!("{actual_err}"), - "Kind::Error: test failed" - ); - } - Kind::Send(gas_opt, expected_mid) => { - let actual_mid_res = match gas_opt { - Some(gas) => msg::send_with_gas_delayed(msg::source(), b"payload", gas, 0, 0), - None => msg::send_delayed(msg::source(), b"payload", 0, 0), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::Send: mid test failed" - ); - } - Kind::SendInput(gas_opt, expected_mid) => { - let actual_mid_res = match gas_opt { - Some(gas) => msg::send_input_with_gas_delayed(msg::source(), gas, 0, .., 0), - None => msg::send_input_delayed(msg::source(), 0, .., 0), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::SendInput: mid test failed" - ); - } - Kind::SendPushInput(expected_mid) => { - // Sending these 2 to increase internal handler returned by `send_init`. - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - - let handle = MessageHandle::init().expect("internal error: failed send init"); - - // check handle - handle - .push_input(0..) - .expect("internal error: push_input failed"); - - let actual_mid_res = handle.commit_delayed(msg::source(), 0, 0); - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::SendPushInput: mid test failed" - ); - } - Kind::SendRaw(payload, gas_opt, expected_mid) => { - // Sending these 2 to increase internal handler returned by `send_init`. - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - - let handle = MessageHandle::init().expect("internal error: failed send init"); - // check handle - handle - .push(payload) - .expect("internal error: failed send_push"); - let actual_mid_res = match gas_opt { - Some(gas) => handle.commit_with_gas_delayed(msg::source(), gas, 0, 0), - None => handle.commit_delayed(msg::source(), 0, 0), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::SendRaw: mid test failed" - ); - } - Kind::Size(expected_size) => { - let actual_size = msg::size(); - assert_eq!( - expected_size as usize, actual_size, - "Kind::Size: size test failed" - ); - } - Kind::MessageId(expected_mid) => { - let actual_mid: [u8; 32] = msg::id().into(); - assert_eq!(expected_mid, actual_mid, "Kind::MessageId: mid test failed"); - } - Kind::PayProgramRent(program_id, rent, expected) => { - let (unused_value, paid_block_count) = - exec::pay_program_rent(program_id.into(), rent) - .expect(super::PAY_PROGRAM_RENT_EXPECT); - if let Some((expected_unused_value, expected_paid_block_count)) = expected { - assert_eq!(unused_value, expected_unused_value); - assert_eq!(paid_block_count, expected_paid_block_count); - } - } - Kind::ProgramId(expected_pid) => { - let actual_pid: [u8; 32] = exec::program_id().into(); - assert_eq!(expected_pid, actual_pid, "Kind::ProgramId: pid test failed"); - } - Kind::Source(expected_actor) => { - let actual_actor: [u8; 32] = msg::source().into(); - assert_eq!( - expected_actor, actual_actor, - "Kind::Source: actor test failed" - ); - } - Kind::Value(expected_value) => { - let actual_value = msg::value(); - assert_eq!( - expected_value, actual_value, - "Kind::Value: value test failed" - ); - } - Kind::ValueAvailable(expected_value) => { - let _ = msg::send_delayed(msg::source(), b"payload", 2000, 0); - let actual_value = exec::value_available(); - assert_eq!( - expected_value, actual_value, - "Kind::ValueAvailable: value test failed" - ); - } - Kind::Reply(gas_opt, expected_mid) => { - let actual_mid_res = match gas_opt { - Some(gas) => msg::reply_with_gas(b"payload", gas, 0), - None => msg::reply(b"payload", 0), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::Reply: mid test failed" - ); - } - Kind::ReplyRaw(payload, gas_opt, expected_mid) => { - msg::reply_push(payload).expect("internal error: failed reply push"); - let actual_mid_res = match gas_opt { - Some(gas) => msg::reply_commit_with_gas(gas, 0), - None => msg::reply_commit(0), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::ReplyRaw: mid test failed" - ); - } - Kind::ReplyInput(gas_opt, expected_mid) => { - let actual_mid_res = match gas_opt { - Some(gas) => msg::reply_input_with_gas(gas, 0, ..), - None => msg::reply_input(0, ..), - }; - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::ReplyInput: mid test failed" - ); - } - Kind::ReplyPushInput(expected_mid) => { - msg::reply_push_input(..).expect("internal error: reply_push_input failed"); - let actual_mid_res = msg::reply_commit(0); - assert_eq!( - Ok(expected_mid.into()), - actual_mid_res, - "Kind::ReplyPushInput: mid test failed" - ); - } - Kind::ReplyDetails(..) => { - // Actual test in handle reply, here just sends a reply - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - // To prevent from sending to mailbox "ok" message - exec::leave(); - } - Kind::SignalDetails => { - if unsafe { DO_PANIC } { - // issue a signal - panic!(); - } else { - unsafe { - SIGNAL_DETAILS = ( - msg::id(), - SignalCode::Execution(SimpleExecutionError::UserspacePanic), - msg::source(), - ); - DO_PANIC = true; - } - exec::system_reserve_gas(1_000_000_000).unwrap(); - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - exec::wait_for(2); - } - } - Kind::SignalDetailsWake => { - panic!("must be called in handle_reply"); - } - Kind::BlockHeight(expected_height) => { - let actual_height = exec::block_height(); - assert_eq!( - expected_height, actual_height, - "Kind::BlockHeight:: block height test failed" - ); - } - Kind::BlockTimestamp(expected_timestamp) => { - let actual_timestamp = exec::block_timestamp(); - assert_eq!( - expected_timestamp, actual_timestamp, - "Kind::BlockTimestamp:: block timestamp test failed" - ); - } - Kind::Reserve(expected_id) => { - // do 2 reservations to increase internal nonce - let _ = ReservationId::reserve(10_000, 3); - let _ = ReservationId::reserve(20_000, 5); - let actual_id = - ReservationId::reserve(30_000, 7).expect("internal error: reservation failed"); - assert_eq!( - expected_id, - actual_id.encode(), - "Kind::Reserve: reserve gas test failed" - ); - } - Kind::Unreserve(expected_amount) => { - let reservation = ReservationId::reserve(expected_amount, 3) - .expect("internal error: reservation failed"); - let actual_amount = reservation.unreserve(); - assert_eq!( - Ok(expected_amount), - actual_amount, - "Kind::Unreserve: unreserve gas test failed" - ); - } - Kind::Random(salt, (expected_hash, expected_bn)) => { - let (actual_hash, actual_bn) = - exec::random(salt).expect("internal error: random call failed"); - assert_eq!(expected_hash, actual_hash, "Kind::Random: hash test failed"); - assert_eq!(expected_bn, actual_bn, "Kind::Random: bn test failed"); - } - Kind::GasAvailable(lower, upper) => { - let gas_available = exec::gas_available(); - assert!( - gas_available >= lower, - "Kind::GasAvailable: lower bound test failed" - ); - assert!( - gas_available <= upper, - "Kind::GasAvailable: upper bound test failed" - ); - } - Kind::ReservationSend(expected_mid) => { - let reservation_id = - ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); - let actual_mid = msg::send_bytes_delayed_from_reservation( - reservation_id, - msg::source(), - b"", - 0, - 0, - ); - assert_eq!( - Ok(expected_mid.into()), - actual_mid, - "Kind::ReservationSend: mid test failed" - ); - } - Kind::ReservationSendRaw(payload, expected_mid) => { - let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); - let reservation_id = - ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); - - let handle = MessageHandle::init().expect("internal error: failed send init"); - // check handle - handle - .push(payload) - .expect("internal error: failed send_push"); - let actual_mid = - handle.commit_delayed_from_reservation(reservation_id, msg::source(), 0, 0); - assert_eq!( - Ok(expected_mid.into()), - actual_mid, - "Kind::ReservationSendRaw: mid test failed" - ); - } - Kind::ReservationReply(expected_mid) => { - let reservation_id = - ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); - let actual_mid = msg::reply_bytes_from_reservation(reservation_id, b"", 0); - assert_eq!( - Ok(expected_mid.into()), - actual_mid, - "Kind::ReservationReply: mid test failed" - ); - } - Kind::ReservationReplyCommit(payload, expected_mid) => { - let reservation_id = - ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); - msg::reply_push(payload).expect("internal error: failed reply push"); - let actual_mid = msg::reply_commit_from_reservation(reservation_id, 0); - assert_eq!( - Ok(expected_mid.into()), - actual_mid, - "Kind::ReservationReplyCommit: mid test failed" - ); - } - Kind::SystemReserveGas(amount) => { - exec::system_reserve_gas(amount).expect("Kind::SystemReserveGas: call test failed"); - // The only case with wait, so we send report before ending execution, instead of - // waking the message - msg::send_delayed(msg::source(), b"ok", 0, 0) - .expect("internal error: report send failed"); - exec::wait_for(2); - } - Kind::ReplyDeposit(amount) => { - let mid = msg::send_bytes(ActorId::zero(), [], 0) - .expect("internal error: failed to send message"); - - exec::reply_deposit(mid, amount).expect("Kind::ReplyDeposit: call test failed"); - } - } - } - - #[no_mangle] - extern "C" fn handle_reply() { - match msg::load() { - Ok(Kind::ReplyDetails(expected_reply_to, expected_reply_code_bytes)) => { - let expected_reply_code = ReplyCode::from_bytes(expected_reply_code_bytes); - let actual_reply_to = msg::reply_to(); - assert_eq!( - Ok(expected_reply_to.into()), - actual_reply_to, - "Kind::ReplyDetails: reply_to test failed" - ); - let actual_reply_code = msg::reply_code(); - assert_eq!( - Ok(expected_reply_code), - actual_reply_code, - "Kind::ReplyDetails: reply code test failed" - ); - - // Report test executed successfully - msg::send_delayed(msg::source(), b"ok", 0, 0) - .expect("internal error: report send failed"); - } - Ok(Kind::SignalDetailsWake) => unsafe { - exec::wake(SIGNAL_DETAILS.0).unwrap(); - }, - _ => panic!("internal error: invalid payload for `handle_reply`"), - } - } - - #[no_mangle] - extern "C" fn handle_signal() { - let (signal_from, signal_code, source) = unsafe { SIGNAL_DETAILS }; - - assert_eq!( - msg::signal_code(), - Ok(Some(signal_code)), - "Kind::SignalDetails: status code test failed" - ); - assert_eq!( - msg::signal_from(), - Ok(signal_from), - "Kind::SignalDetails: signal_from test failed" - ); - - msg::send_delayed(source, b"ok", 0, 0).expect("internal error: report send failed"); - } -} +mod wasm; diff --git a/examples/sys-calls/src/wasm.rs b/examples/sys-calls/src/wasm.rs new file mode 100644 index 00000000000..06a194c8fdc --- /dev/null +++ b/examples/sys-calls/src/wasm.rs @@ -0,0 +1,432 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Kind; +use gstd::{ + errors::{ReplyCode, SignalCode, SimpleExecutionError}, + exec, format, + msg::{self, MessageHandle}, + prog, ActorId, CodeId, MessageId, ReservationId, Vec, +}; +use parity_scale_codec::Encode; + +static mut CODE_ID: CodeId = CodeId::new([0u8; 32]); +static mut SIGNAL_DETAILS: (MessageId, SignalCode, ActorId) = ( + MessageId::new([0; 32]), + SignalCode::Execution(SimpleExecutionError::Unsupported), + ActorId::zero(), +); +static mut DO_PANIC: bool = false; + +#[no_mangle] +extern "C" fn init() { + let code_id_bytes: [u8; 32] = msg::load().expect("internal error: invalid payload"); + + unsafe { CODE_ID = code_id_bytes.into() }; +} + +#[no_mangle] +extern "C" fn handle() { + let syscall_kinds: Vec = msg::load().expect("internal error: invalid payload"); + for syscall_kind in syscall_kinds { + process(syscall_kind); + } + + // Report test executed successfully + msg::send_delayed(msg::source(), b"ok", 0, 0).expect("internal error: report send failed"); +} + +fn process(syscall_kind: Kind) { + match syscall_kind { + Kind::CreateProgram(salt, gas_opt, (expected_mid, expected_pid)) => { + let salt = salt.to_le_bytes(); + let res = match gas_opt { + Some(gas) => prog::create_program_bytes_with_gas_delayed( + unsafe { CODE_ID }, + salt, + "payload", + gas, + 0, + 0, + ), + None => { + prog::create_program_bytes_delayed(unsafe { CODE_ID }, salt, "payload", 0, 0) + } + }; + let (actual_mid, actual_pid) = res.expect("internal error: create program failed"); + let actual_mid: [u8; 32] = actual_mid.into(); + let actual_pid: [u8; 32] = actual_pid.into(); + assert_eq!( + expected_mid, actual_mid, + "Kind::CreateProgram: mid test failed" + ); + assert_eq!( + expected_pid, actual_pid, + "Kind::CreateProgram: pid test failed" + ); + } + Kind::Error(message_value, expected_err) => { + let actual_err = msg::reply(b"", message_value).expect_err("not enough balance"); + assert_eq!( + expected_err, + format!("{actual_err}"), + "Kind::Error: test failed" + ); + } + Kind::Send(gas_opt, expected_mid) => { + let actual_mid_res = match gas_opt { + Some(gas) => msg::send_with_gas_delayed(msg::source(), b"payload", gas, 0, 0), + None => msg::send_delayed(msg::source(), b"payload", 0, 0), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::Send: mid test failed" + ); + } + Kind::SendInput(gas_opt, expected_mid) => { + let actual_mid_res = match gas_opt { + Some(gas) => msg::send_input_with_gas_delayed(msg::source(), gas, 0, .., 0), + None => msg::send_input_delayed(msg::source(), 0, .., 0), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::SendInput: mid test failed" + ); + } + Kind::SendPushInput(expected_mid) => { + // Sending these 2 to increase internal handler returned by `send_init`. + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + + let handle = MessageHandle::init().expect("internal error: failed send init"); + + // check handle + handle + .push_input(0..) + .expect("internal error: push_input failed"); + + let actual_mid_res = handle.commit_delayed(msg::source(), 0, 0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::SendPushInput: mid test failed" + ); + } + Kind::SendRaw(payload, gas_opt, expected_mid) => { + // Sending these 2 to increase internal handler returned by `send_init`. + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + + let handle = MessageHandle::init().expect("internal error: failed send init"); + // check handle + handle + .push(payload) + .expect("internal error: failed send_push"); + let actual_mid_res = match gas_opt { + Some(gas) => handle.commit_with_gas_delayed(msg::source(), gas, 0, 0), + None => handle.commit_delayed(msg::source(), 0, 0), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::SendRaw: mid test failed" + ); + } + Kind::Size(expected_size) => { + let actual_size = msg::size(); + assert_eq!( + expected_size as usize, actual_size, + "Kind::Size: size test failed" + ); + } + Kind::MessageId(expected_mid) => { + let actual_mid: [u8; 32] = msg::id().into(); + assert_eq!(expected_mid, actual_mid, "Kind::MessageId: mid test failed"); + } + Kind::PayProgramRent(program_id, rent, expected) => { + let (unused_value, paid_block_count) = exec::pay_program_rent(program_id.into(), rent) + .expect(super::PAY_PROGRAM_RENT_EXPECT); + if let Some((expected_unused_value, expected_paid_block_count)) = expected { + assert_eq!(unused_value, expected_unused_value); + assert_eq!(paid_block_count, expected_paid_block_count); + } + } + Kind::ProgramId(expected_pid) => { + let actual_pid: [u8; 32] = exec::program_id().into(); + assert_eq!(expected_pid, actual_pid, "Kind::ProgramId: pid test failed"); + } + Kind::Source(expected_actor) => { + let actual_actor: [u8; 32] = msg::source().into(); + assert_eq!( + expected_actor, actual_actor, + "Kind::Source: actor test failed" + ); + } + Kind::Value(expected_value) => { + let actual_value = msg::value(); + assert_eq!( + expected_value, actual_value, + "Kind::Value: value test failed" + ); + } + Kind::ValueAvailable(expected_value) => { + let _ = msg::send_delayed(msg::source(), b"payload", 2000, 0); + let actual_value = exec::value_available(); + assert_eq!( + expected_value, actual_value, + "Kind::ValueAvailable: value test failed" + ); + } + Kind::Reply(gas_opt, expected_mid) => { + let actual_mid_res = match gas_opt { + Some(gas) => msg::reply_with_gas(b"payload", gas, 0), + None => msg::reply(b"payload", 0), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::Reply: mid test failed" + ); + } + Kind::ReplyRaw(payload, gas_opt, expected_mid) => { + msg::reply_push(payload).expect("internal error: failed reply push"); + let actual_mid_res = match gas_opt { + Some(gas) => msg::reply_commit_with_gas(gas, 0), + None => msg::reply_commit(0), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::ReplyRaw: mid test failed" + ); + } + Kind::ReplyInput(gas_opt, expected_mid) => { + let actual_mid_res = match gas_opt { + Some(gas) => msg::reply_input_with_gas(gas, 0, ..), + None => msg::reply_input(0, ..), + }; + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::ReplyInput: mid test failed" + ); + } + Kind::ReplyPushInput(expected_mid) => { + msg::reply_push_input(..).expect("internal error: reply_push_input failed"); + let actual_mid_res = msg::reply_commit(0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid_res, + "Kind::ReplyPushInput: mid test failed" + ); + } + Kind::ReplyDetails(..) => { + // Actual test in handle reply, here just sends a reply + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + // To prevent from sending to mailbox "ok" message + exec::leave(); + } + Kind::SignalDetails => { + if unsafe { DO_PANIC } { + // issue a signal + panic!(); + } else { + unsafe { + SIGNAL_DETAILS = ( + msg::id(), + SignalCode::Execution(SimpleExecutionError::UserspacePanic), + msg::source(), + ); + DO_PANIC = true; + } + exec::system_reserve_gas(1_000_000_000).unwrap(); + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + exec::wait_for(2); + } + } + Kind::SignalDetailsWake => { + panic!("must be called in handle_reply"); + } + Kind::BlockHeight(expected_height) => { + let actual_height = exec::block_height(); + assert_eq!( + expected_height, actual_height, + "Kind::BlockHeight:: block height test failed" + ); + } + Kind::BlockTimestamp(expected_timestamp) => { + let actual_timestamp = exec::block_timestamp(); + assert_eq!( + expected_timestamp, actual_timestamp, + "Kind::BlockTimestamp:: block timestamp test failed" + ); + } + Kind::Reserve(expected_id) => { + // do 2 reservations to increase internal nonce + let _ = ReservationId::reserve(10_000, 3); + let _ = ReservationId::reserve(20_000, 5); + let actual_id = + ReservationId::reserve(30_000, 7).expect("internal error: reservation failed"); + assert_eq!( + expected_id, + actual_id.encode(), + "Kind::Reserve: reserve gas test failed" + ); + } + Kind::Unreserve(expected_amount) => { + let reservation = ReservationId::reserve(expected_amount, 3) + .expect("internal error: reservation failed"); + let actual_amount = reservation.unreserve(); + assert_eq!( + Ok(expected_amount), + actual_amount, + "Kind::Unreserve: unreserve gas test failed" + ); + } + Kind::Random(salt, (expected_hash, expected_bn)) => { + let (actual_hash, actual_bn) = + exec::random(salt).expect("internal error: random call failed"); + assert_eq!(expected_hash, actual_hash, "Kind::Random: hash test failed"); + assert_eq!(expected_bn, actual_bn, "Kind::Random: bn test failed"); + } + Kind::GasAvailable(lower, upper) => { + let gas_available = exec::gas_available(); + assert!( + gas_available >= lower, + "Kind::GasAvailable: lower bound test failed" + ); + assert!( + gas_available <= upper, + "Kind::GasAvailable: upper bound test failed" + ); + } + Kind::ReservationSend(expected_mid) => { + let reservation_id = + ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); + let actual_mid = + msg::send_bytes_delayed_from_reservation(reservation_id, msg::source(), b"", 0, 0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid, + "Kind::ReservationSend: mid test failed" + ); + } + Kind::ReservationSendRaw(payload, expected_mid) => { + let _ = msg::send_delayed(msg::source(), b"payload", 0, 0); + let reservation_id = + ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); + + let handle = MessageHandle::init().expect("internal error: failed send init"); + // check handle + handle + .push(payload) + .expect("internal error: failed send_push"); + let actual_mid = + handle.commit_delayed_from_reservation(reservation_id, msg::source(), 0, 0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid, + "Kind::ReservationSendRaw: mid test failed" + ); + } + Kind::ReservationReply(expected_mid) => { + let reservation_id = + ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); + let actual_mid = msg::reply_bytes_from_reservation(reservation_id, b"", 0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid, + "Kind::ReservationReply: mid test failed" + ); + } + Kind::ReservationReplyCommit(payload, expected_mid) => { + let reservation_id = + ReservationId::reserve(25_000_000_000, 1).expect("reservation failed"); + msg::reply_push(payload).expect("internal error: failed reply push"); + let actual_mid = msg::reply_commit_from_reservation(reservation_id, 0); + assert_eq!( + Ok(expected_mid.into()), + actual_mid, + "Kind::ReservationReplyCommit: mid test failed" + ); + } + Kind::SystemReserveGas(amount) => { + exec::system_reserve_gas(amount).expect("Kind::SystemReserveGas: call test failed"); + // The only case with wait, so we send report before ending execution, instead of + // waking the message + msg::send_delayed(msg::source(), b"ok", 0, 0) + .expect("internal error: report send failed"); + exec::wait_for(2); + } + Kind::ReplyDeposit(amount) => { + let mid = msg::send_bytes(ActorId::zero(), [], 0) + .expect("internal error: failed to send message"); + + exec::reply_deposit(mid, amount).expect("Kind::ReplyDeposit: call test failed"); + } + } +} + +#[no_mangle] +extern "C" fn handle_reply() { + match msg::load() { + Ok(Kind::ReplyDetails(expected_reply_to, expected_reply_code_bytes)) => { + let expected_reply_code = ReplyCode::from_bytes(expected_reply_code_bytes); + let actual_reply_to = msg::reply_to(); + assert_eq!( + Ok(expected_reply_to.into()), + actual_reply_to, + "Kind::ReplyDetails: reply_to test failed" + ); + let actual_reply_code = msg::reply_code(); + assert_eq!( + Ok(expected_reply_code), + actual_reply_code, + "Kind::ReplyDetails: reply code test failed" + ); + + // Report test executed successfully + msg::send_delayed(msg::source(), b"ok", 0, 0) + .expect("internal error: report send failed"); + } + Ok(Kind::SignalDetailsWake) => unsafe { + exec::wake(SIGNAL_DETAILS.0).unwrap(); + }, + _ => panic!("internal error: invalid payload for `handle_reply`"), + } +} + +#[no_mangle] +extern "C" fn handle_signal() { + let (signal_from, signal_code, source) = unsafe { SIGNAL_DETAILS }; + + assert_eq!( + msg::signal_code(), + Ok(Some(signal_code)), + "Kind::SignalDetails: status code test failed" + ); + assert_eq!( + msg::signal_from(), + Ok(signal_from), + "Kind::SignalDetails: signal_from test failed" + ); + + msg::send_delayed(source, b"ok", 0, 0).expect("internal error: report send failed"); +} diff --git a/examples/syscall-error/Cargo.toml b/examples/syscall-error/Cargo.toml index 885b3139f81..b454bad67bc 100644 --- a/examples/syscall-error/Cargo.toml +++ b/examples/syscall-error/Cargo.toml @@ -2,12 +2,13 @@ name = "demo-syscall-error" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true gstd.workspace = true gsys.workspace = true @@ -17,8 +18,6 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/syscall-error/src/lib.rs b/examples/syscall-error/src/lib.rs index a22dca29e2e..216ee1b7aa1 100644 --- a/examples/syscall-error/src/lib.rs +++ b/examples/syscall-error/src/lib.rs @@ -18,8 +18,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -use gstd::{msg, prelude::*, ActorId}; - #[cfg(feature = "std")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -27,18 +25,9 @@ mod code { #[cfg(feature = "std")] pub use code::WASM_BINARY_OPT as WASM_BINARY; -use gstd::errors::{Error, ExtError, MessageError}; - -#[no_mangle] -extern "C" fn init() { - let res = msg::send(ActorId::default(), "dummy", 250); - assert_eq!( - res, - Err(Error::Core( - ExtError::Message(MessageError::InsufficientValue).into() - )) - ); -} + +#[cfg(not(feature = "std"))] +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/syscall-error/src/wasm.rs b/examples/syscall-error/src/wasm.rs new file mode 100644 index 00000000000..74a45d9a3aa --- /dev/null +++ b/examples/syscall-error/src/wasm.rs @@ -0,0 +1,35 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{ + errors::{Error, ExtError, MessageError}, + msg, + prelude::*, + ActorId, +}; + +#[no_mangle] +extern "C" fn init() { + let res = msg::send(ActorId::default(), "dummy", 250); + assert_eq!( + res, + Err(Error::Core( + ExtError::Message(MessageError::InsufficientValue).into() + )) + ); +} diff --git a/examples/vec/Cargo.toml b/examples/vec/Cargo.toml index 1d9d8417fba..176929ad803 100644 --- a/examples/vec/Cargo.toml +++ b/examples/vec/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd = { workspace = true, features = ["debug"] } diff --git a/examples/vec/src/lib.rs b/examples/vec/src/lib.rs index 90ea46965f4..fbc08d47a0b 100644 --- a/examples/vec/src/lib.rs +++ b/examples/vec/src/lib.rs @@ -27,39 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{debug, msg, prelude::*}; - - static mut MESSAGE_LOG: Vec = vec![]; - - #[no_mangle] - extern "C" fn handle() { - let size = msg::load::().expect("Failed to load `i32`") as usize; - - let request = format!("Request: size = {size}"); - - debug!("{request}"); - unsafe { MESSAGE_LOG.push(request) }; - - let vec = vec![42u8; size]; - let last_idx = size - 1; - - debug!("vec.len() = {:?}", vec.len()); - debug!( - "vec[{last_idx}]: {:p} -> {:#04x}", - &vec[last_idx], vec[last_idx] - ); - - msg::reply(size as i32, 0).expect("Failed to send reply"); - - // The test idea is to allocate two wasm pages and check this allocation, - // so we must skip `v` destruction. - core::mem::forget(vec); - - let requests_amount = unsafe { MESSAGE_LOG.len() }; - debug!("Total requests amount: {requests_amount}"); - unsafe { - MESSAGE_LOG.iter().for_each(|log| debug!("{log}")); - } - } -} +mod wasm; diff --git a/examples/vec/src/wasm.rs b/examples/vec/src/wasm.rs new file mode 100644 index 00000000000..fe0eeb50827 --- /dev/null +++ b/examples/vec/src/wasm.rs @@ -0,0 +1,52 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{debug, msg, prelude::*}; + +static mut MESSAGE_LOG: Vec = vec![]; + +#[no_mangle] +extern "C" fn handle() { + let size = msg::load::().expect("Failed to load `i32`") as usize; + + let request = format!("Request: size = {size}"); + + debug!("{request}"); + unsafe { MESSAGE_LOG.push(request) }; + + let vec = vec![42u8; size]; + let last_idx = size - 1; + + debug!("vec.len() = {:?}", vec.len()); + debug!( + "vec[{last_idx}]: {:p} -> {:#04x}", + &vec[last_idx], vec[last_idx] + ); + + msg::reply(size as i32, 0).expect("Failed to send reply"); + + // The test idea is to allocate two wasm pages and check this allocation, + // so we must skip `v` destruction. + core::mem::forget(vec); + + let requests_amount = unsafe { MESSAGE_LOG.len() }; + debug!("Total requests amount: {requests_amount}"); + unsafe { + MESSAGE_LOG.iter().for_each(|log| debug!("{log}")); + } +} diff --git a/examples/wait-timeout/Cargo.toml b/examples/wait-timeout/Cargo.toml index dcd5f2521bd..c1e7c4e47a0 100644 --- a/examples/wait-timeout/Cargo.toml +++ b/examples/wait-timeout/Cargo.toml @@ -2,22 +2,19 @@ name = "demo-wait-timeout" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } gstd.workspace = true -futures = { version = "0.3", default-features = false, features = ["alloc"] } +futures.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[dev-dependencies] - -[lib] - [features] debug = ["gstd/debug"] std = ["parity-scale-codec/std"] diff --git a/examples/wait-timeout/src/lib.rs b/examples/wait-timeout/src/lib.rs index 51c746d1082..2d9e85303aa 100644 --- a/examples/wait-timeout/src/lib.rs +++ b/examples/wait-timeout/src/lib.rs @@ -30,9 +30,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; // Re-exports for testing pub fn default_wait_up_to_duration() -> u32 { diff --git a/examples/wait-timeout/src/code.rs b/examples/wait-timeout/src/wasm.rs similarity index 84% rename from examples/wait-timeout/src/code.rs rename to examples/wait-timeout/src/wasm.rs index 7f0ff9e13ba..6bb31bafdbf 100644 --- a/examples/wait-timeout/src/code.rs +++ b/examples/wait-timeout/src/wasm.rs @@ -1,5 +1,22 @@ -use crate::Command; +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Command; use futures::future; use gstd::{exec, msg, MessageId}; diff --git a/examples/wait/Cargo.toml b/examples/wait/Cargo.toml index 172e9f4cc06..1ef9502fc9c 100644 --- a/examples/wait/Cargo.toml +++ b/examples/wait/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true diff --git a/examples/wait/src/lib.rs b/examples/wait/src/lib.rs index 72b8ae64946..fbc08d47a0b 100644 --- a/examples/wait/src/lib.rs +++ b/examples/wait/src/lib.rs @@ -27,40 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - // for panic/oom handlers - extern crate gstd; - - use gcore::{exec, msg, MessageId}; - - static mut STATE: u32 = 0; - static mut MSG_ID_1: MessageId = MessageId::zero(); - static mut MSG_ID_2: MessageId = MessageId::zero(); - - #[no_mangle] - extern "C" fn handle() { - let state = unsafe { &mut STATE }; - gstd::debug!("{state}"); - - match *state { - 0 => { - *state = 1; - unsafe { MSG_ID_1 = msg::id() }; - exec::wait(); - } - 1 => { - *state = 2; - unsafe { MSG_ID_2 = msg::id() }; - exec::wait(); - } - 2 => { - *state = 3; - exec::wake(unsafe { MSG_ID_1 }).unwrap(); - exec::wake(unsafe { MSG_ID_2 }).unwrap(); - } - _ => { - msg::send(msg::source(), msg::id().as_slice(), 0).unwrap(); - } - } - } -} +mod wasm; diff --git a/examples/wait/src/wasm.rs b/examples/wait/src/wasm.rs new file mode 100644 index 00000000000..b6c0a6afafc --- /dev/null +++ b/examples/wait/src/wasm.rs @@ -0,0 +1,53 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// for panic/oom handlers +extern crate gstd; + +use gcore::{exec, msg, MessageId}; + +static mut STATE: u32 = 0; +static mut MSG_ID_1: MessageId = MessageId::zero(); +static mut MSG_ID_2: MessageId = MessageId::zero(); + +#[no_mangle] +extern "C" fn handle() { + let state = unsafe { &mut STATE }; + gstd::debug!("{state}"); + + match *state { + 0 => { + *state = 1; + unsafe { MSG_ID_1 = msg::id() }; + exec::wait(); + } + 1 => { + *state = 2; + unsafe { MSG_ID_2 = msg::id() }; + exec::wait(); + } + 2 => { + *state = 3; + exec::wake(unsafe { MSG_ID_1 }).unwrap(); + exec::wake(unsafe { MSG_ID_2 }).unwrap(); + } + _ => { + msg::send(msg::source(), msg::id().as_slice(), 0).unwrap(); + } + } +} diff --git a/examples/wait_wake/Cargo.toml b/examples/wait_wake/Cargo.toml index 0f23aad78dc..b92655788c4 100644 --- a/examples/wait_wake/Cargo.toml +++ b/examples/wait_wake/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true -parity-scale-codec = { workspace = true, features = ["derive"] } +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true diff --git a/examples/wait_wake/src/lib.rs b/examples/wait_wake/src/lib.rs index fd86d83b2d5..cbfe0514f7f 100644 --- a/examples/wait_wake/src/lib.rs +++ b/examples/wait_wake/src/lib.rs @@ -35,41 +35,7 @@ pub enum Request { } #[cfg(not(feature = "std"))] -mod wasm { - use super::*; - use gstd::{collections::BTreeMap, exec, msg, prelude::*, MessageId}; - - static mut ECHOES: Option> = None; - - fn process_request(request: Request) { - match request { - Request::EchoWait(n) => { - unsafe { - ECHOES - .get_or_insert_with(BTreeMap::new) - .insert(msg::id(), n) - }; - exec::wait(); - } - Request::Wake(id) => exec::wake(id.into()).unwrap(), - } - } - - #[no_mangle] - extern "C" fn init() { - msg::reply((), 0).unwrap(); - } - - #[no_mangle] - extern "C" fn handle() { - if let Some(reply) = unsafe { ECHOES.get_or_insert_with(BTreeMap::new).remove(&msg::id()) } - { - msg::reply(reply, 0).unwrap(); - } else { - msg::load::().map(process_request).unwrap(); - } - } -} +mod wasm; #[cfg(test)] mod tests { diff --git a/examples/wait_wake/src/wasm.rs b/examples/wait_wake/src/wasm.rs new file mode 100644 index 00000000000..a4cd16f8ed2 --- /dev/null +++ b/examples/wait_wake/src/wasm.rs @@ -0,0 +1,50 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::Request; +use gstd::{collections::BTreeMap, exec, msg, prelude::*, MessageId}; + +static mut ECHOES: Option> = None; + +fn process_request(request: Request) { + match request { + Request::EchoWait(n) => { + unsafe { + ECHOES + .get_or_insert_with(BTreeMap::new) + .insert(msg::id(), n) + }; + exec::wait(); + } + Request::Wake(id) => exec::wake(id.into()).unwrap(), + } +} + +#[no_mangle] +extern "C" fn init() { + msg::reply((), 0).unwrap(); +} + +#[no_mangle] +extern "C" fn handle() { + if let Some(reply) = unsafe { ECHOES.get_or_insert_with(BTreeMap::new).remove(&msg::id()) } { + msg::reply(reply, 0).unwrap(); + } else { + msg::load::().map(process_request).unwrap(); + } +} diff --git a/examples/waiter/Cargo.toml b/examples/waiter/Cargo.toml index 3090fc3f7a0..546e34a37f7 100644 --- a/examples/waiter/Cargo.toml +++ b/examples/waiter/Cargo.toml @@ -4,13 +4,14 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] -parity-scale-codec = { workspace = true, features = ["derive"] } futures.workspace = true gstd.workspace = true gcore.workspace = true +parity-scale-codec.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -18,9 +19,7 @@ gear-wasm-builder.workspace = true [dev-dependencies] gtest.workspace = true gear-core.workspace = true -demo-waiter = { path = ".", features = ["debug"] } - -[lib] +demo-waiter = { workspace = true, features = ["debug"] } [features] debug = ["gstd/debug"] diff --git a/examples/waiter/src/lib.rs b/examples/waiter/src/lib.rs index b7d4619f789..7f9122757eb 100644 --- a/examples/waiter/src/lib.rs +++ b/examples/waiter/src/lib.rs @@ -35,9 +35,7 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "wasm-wrapper"))] -mod wasm { - include! {"./code.rs"} -} +mod wasm; #[cfg(feature = "std")] pub fn system_reserve() -> u64 { diff --git a/examples/waiter/src/code.rs b/examples/waiter/src/wasm.rs similarity index 94% rename from examples/waiter/src/code.rs rename to examples/waiter/src/wasm.rs index 7a68dd08957..5c5a848c613 100644 --- a/examples/waiter/src/code.rs +++ b/examples/waiter/src/wasm.rs @@ -44,29 +44,33 @@ async fn main() { .expect("send message failed") .exactly(Some(duration)) .expect("Invalid wait duration.") - .await; + .await + .expect("Failed to send message"); } Command::SendUpTo(to, duration) => { msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .up_to(Some(duration)) .expect("Invalid wait duration.") - .await; + .await + .expect("Failed to send message"); } Command::SendUpToWait(to, duration) => { msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .up_to(Some(duration)) .expect("Invalid wait duration.") - .await; + .await + .expect("Failed to send message"); // after waking, wait again. msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") - .await; + .await + .expect("Failed to send message"); } Command::SendAndWaitFor(duration, to) => { - msg::send(to.into(), b"ping", 0); + msg::send(to.into(), b"ping", 0).expect("send message failed"); exec::wait_for(duration); } Command::ReplyAndWait(subcommand) => { @@ -89,7 +93,6 @@ async fn main() { SleepForWaitType::Any => { future::select_all(sleep_futures).await; } - _ => unreachable!(), } msg::send( msg::source(), @@ -195,9 +198,7 @@ async fn process_lock_continuation( match continuation { LockContinuation::Nothing => {} LockContinuation::SleepFor(duration) => exec::sleep_for(duration).await, - LockContinuation::MoveToStatic => unsafe { - *static_lock_guard = Some(lock_guard); - }, + LockContinuation::MoveToStatic => *static_lock_guard = Some(lock_guard), LockContinuation::Wait => exec::wait(), LockContinuation::Forget => { gstd::mem::forget(lock_guard); diff --git a/examples/waiting-proxy/Cargo.toml b/examples/waiting-proxy/Cargo.toml index 9ae7ce44ea6..076c2520096 100644 --- a/examples/waiting-proxy/Cargo.toml +++ b/examples/waiting-proxy/Cargo.toml @@ -2,9 +2,10 @@ name = "demo-waiting-proxy" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" -workspace = "../../" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gstd.workspace = true @@ -12,8 +13,6 @@ gstd.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] std = [] diff --git a/examples/waiting-proxy/src/lib.rs b/examples/waiting-proxy/src/lib.rs index ade290c2ec9..b20b99554f5 100644 --- a/examples/waiting-proxy/src/lib.rs +++ b/examples/waiting-proxy/src/lib.rs @@ -27,31 +27,4 @@ mod code { pub use code::WASM_BINARY_OPT as WASM_BINARY; #[cfg(not(feature = "std"))] -mod wasm { - use gstd::{msg, ActorId}; - - static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); - static mut REPLY_DEPOSIT: u64 = 0; - - #[gstd::async_main] - async fn main() { - let input = msg::load_bytes().expect("Failed to load payload bytes"); - if let Ok(outcome) = - msg::send_bytes_for_reply(unsafe { DESTINATION }, input, 0, unsafe { REPLY_DEPOSIT }) - .expect("Error sending message") - .await - { - msg::reply_bytes(outcome, 0).expect("Failed to send reply"); - } - } - - #[no_mangle] - extern "C" fn init() { - let (destination, reply_deposit) = msg::load().expect("Expecting a contract address"); - unsafe { - DESTINATION = destination; - REPLY_DEPOSIT = reply_deposit; - } - msg::reply((), 0).expect("Failed to send reply"); - } -} +mod wasm; diff --git a/examples/waiting-proxy/src/wasm.rs b/examples/waiting-proxy/src/wasm.rs new file mode 100644 index 00000000000..833c5e2a293 --- /dev/null +++ b/examples/waiting-proxy/src/wasm.rs @@ -0,0 +1,44 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gstd::{msg, ActorId}; + +static mut DESTINATION: ActorId = ActorId::new([0u8; 32]); +static mut REPLY_DEPOSIT: u64 = 0; + +#[gstd::async_main] +async fn main() { + let input = msg::load_bytes().expect("Failed to load payload bytes"); + if let Ok(outcome) = + msg::send_bytes_for_reply(unsafe { DESTINATION }, input, 0, unsafe { REPLY_DEPOSIT }) + .expect("Error sending message") + .await + { + msg::reply_bytes(outcome, 0).expect("Failed to send reply"); + } +} + +#[no_mangle] +extern "C" fn init() { + let (destination, reply_deposit) = msg::load().expect("Expecting a contract address"); + unsafe { + DESTINATION = destination; + REPLY_DEPOSIT = reply_deposit; + } + msg::reply((), 0).expect("Failed to send reply"); +} diff --git a/examples/wat/Cargo.toml b/examples/wat/Cargo.toml index 9ea0380a9bb..f4cfbee18e0 100644 --- a/examples/wat/Cargo.toml +++ b/examples/wat/Cargo.toml @@ -4,8 +4,9 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license.workspace = true -workspace = "../../" +homepage.workspace = true +repository.workspace = true [dependencies] -hex = { workspace = true, features = ["alloc"] } +hex.workspace = true wabt.workspace = true diff --git a/galloc/Cargo.toml b/galloc/Cargo.toml index 3f08c572dd3..fa5f19ddc84 100644 --- a/galloc/Cargo.toml +++ b/galloc/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true [dependencies] # add "checks" feature to enable hard checks in allocator -dlmalloc = { workspace = true, features = ["global"] } +dlmalloc.workspace = true [features] debug = ["dlmalloc/debug"] diff --git a/gclient/src/api/calls.rs b/gclient/src/api/calls.rs index 69e93065b9e..5932d0e0acf 100644 --- a/gclient/src/api/calls.rs +++ b/gclient/src/api/calls.rs @@ -210,7 +210,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -677,7 +677,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -773,7 +773,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -913,7 +913,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -1012,7 +1012,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -1143,7 +1143,7 @@ impl GearApi { let amount = calls.len(); - let tx = self.0.force_batch(calls).await?; + let tx = self.0.calls.force_batch(calls).await?; let mut res = Vec::with_capacity(amount); for event in tx.wait_for_success().await?.iter() { @@ -1251,6 +1251,7 @@ impl GearApi { pub async fn set_code(&self, code: impl AsRef<[u8]>) -> Result { let events = self .0 + .calls .sudo_unchecked_weight( RuntimeCall::System(SystemCall::set_code { code: code.as_ref().to_vec(), @@ -1283,6 +1284,7 @@ impl GearApi { pub async fn set_code_without_checks(&self, code: impl AsRef<[u8]>) -> Result { let events = self .0 + .calls .sudo_unchecked_weight( RuntimeCall::System(SystemCall::set_code_without_checks { code: code.as_ref().to_vec(), @@ -1318,6 +1320,7 @@ impl GearApi { ) -> Result { let events = self .0 + .calls .sudo_unchecked_weight( RuntimeCall::Balances(BalancesCall::set_balance { who: to.into().convert(), diff --git a/gsdk/Cargo.toml b/gsdk/Cargo.toml index 3985dbc5494..9ecf26935dc 100644 --- a/gsdk/Cargo.toml +++ b/gsdk/Cargo.toml @@ -15,9 +15,10 @@ anyhow.workspace = true base64.workspace = true futures-util.workspace = true futures.workspace = true -gear-core.workspace = true +gear-core = { workspace = true, features = [ "std" ] } gear-core-errors.workspace = true hex.workspace = true +indexmap.workspace = true jsonrpsee = { workspace = true, features = [ "http-client", "ws-client" ] } log.workspace = true scale-value.workspace = true @@ -28,6 +29,7 @@ thiserror.workspace = true sp-runtime = { workspace = true, features = [ "std" ] } sp-core.workspace = true gsdk-codegen = { path = "codegen" } +parking_lot.workspace = true # Importing these two libraries for trimming # the the size of the generated file. diff --git a/gsdk/src/backtrace.rs b/gsdk/src/backtrace.rs new file mode 100644 index 00000000000..0478eb07ee8 --- /dev/null +++ b/gsdk/src/backtrace.rs @@ -0,0 +1,101 @@ +// This file is part of Gear. +// +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//! Backtrace support for `gsdk` + +use crate::TxStatus; +use indexmap::IndexMap; +use parking_lot::Mutex; +use sp_core::H256; +use std::{collections::BTreeMap, sync::Arc, time::SystemTime}; + +/// Transaction Status for Backtrace +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum BacktraceStatus { + Future, + Ready, + Broadcast(Vec), + InBlock { + block_hash: H256, + extrinsic_hash: H256, + }, + Retracted { + block_hash: H256, + }, + FinalityTimeout { + block_hash: H256, + }, + Finalized { + block_hash: H256, + extrinsic_hash: H256, + }, + Usurped { + extrinsic_hash: H256, + }, + Dropped, + Invalid, +} + +impl<'s> From<&'s TxStatus> for BacktraceStatus { + fn from(status: &'s TxStatus) -> BacktraceStatus { + match status { + TxStatus::Future => BacktraceStatus::Future, + TxStatus::Ready => BacktraceStatus::Ready, + TxStatus::Broadcast(v) => BacktraceStatus::Broadcast(v.clone()), + TxStatus::InBlock(b) => BacktraceStatus::InBlock { + block_hash: b.block_hash(), + extrinsic_hash: b.extrinsic_hash(), + }, + TxStatus::Retracted(h) => BacktraceStatus::Retracted { block_hash: *h }, + TxStatus::FinalityTimeout(h) => BacktraceStatus::FinalityTimeout { block_hash: *h }, + TxStatus::Finalized(b) => BacktraceStatus::Finalized { + block_hash: b.block_hash(), + extrinsic_hash: b.extrinsic_hash(), + }, + TxStatus::Usurped(h) => BacktraceStatus::Usurped { extrinsic_hash: *h }, + TxStatus::Dropped => BacktraceStatus::Dropped, + TxStatus::Invalid => BacktraceStatus::Invalid, + } + } +} + +/// Backtrace support for transactions +#[derive(Clone, Debug, Default)] +pub struct Backtrace { + inner: Arc>>>, +} + +impl Backtrace { + /// Append status to transaction + pub fn append(&self, tx: H256, status: impl Into) { + let mut inner = self.inner.lock(); + + if let Some(map) = inner.get_mut(&tx) { + map.insert(SystemTime::now(), status.into()); + } else { + let mut map: BTreeMap = Default::default(); + map.insert(SystemTime::now(), status.into()); + inner.insert(tx, map); + }; + } + + /// Get backtrace of transaction + pub fn get(&self, tx: H256) -> Option> { + self.inner.lock().get(&tx).cloned() + } +} diff --git a/gsdk/src/lib.rs b/gsdk/src/lib.rs index f4705522088..3eff02f20a5 100644 --- a/gsdk/src/lib.rs +++ b/gsdk/src/lib.rs @@ -31,9 +31,9 @@ use crate::{ ActiveProgram, }, }; +pub use gear_core::gas::GasInfo; use gear_core::ids::{MessageId, ReservationId}; -use parity_scale_codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; +use parity_scale_codec::Decode; use sp_runtime::AccountId32; use std::collections::HashMap; pub use subxt::dynamic::Value; @@ -43,6 +43,7 @@ use subxt::{ }; mod api; +pub mod backtrace; mod client; pub mod config; mod constants; @@ -71,17 +72,6 @@ pub mod gp { /// Block number type pub type BlockNumber = u32; -/// Information of gas -#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, Serialize, Deserialize)] -pub struct GasInfo { - /// Represents minimum gas limit required for execution. - pub min_limit: u64, - /// Gas amount that we reserve for some other on-chain interactions. - pub reserved: u64, - /// Contains number of gas burned during message processing. - pub burned: u64, -} - /// Gear gas node id. pub type GearGasNodeId = GasNodeId; diff --git a/gsdk/src/signer/calls.rs b/gsdk/src/signer/calls.rs index 2dc3d2bece5..3ab66b71e3c 100644 --- a/gsdk/src/signer/calls.rs +++ b/gsdk/src/signer/calls.rs @@ -17,56 +17,26 @@ // along with this program. If not, see . //! gear api calls -use super::SignerInner; +use super::Inner; use crate::{ config::GearConfig, metadata::{ calls::{BalancesCall, GearCall, SudoCall, UtilityCall}, gear_runtime::RuntimeCall, - runtime_types::{ - frame_system::pallet::Call, - gear_common::{ActiveProgram, Program}, - gear_core::code::InstrumentedCode, - pallet_gear_bank::pallet::BankAccount, - sp_weights::weight_v2::Weight, - }, - storage::{GearBankStorage, GearGasStorage, GearProgramStorage}, - sudo::Event as SudoEvent, - Event, + runtime_types::sp_weights::weight_v2::Weight, }, - signer::SignerRpc, - utils::storage_address_bytes, - Api, BlockNumber, Error, GearGasNode, GearGasNodeId, GearPages, Result, TxInBlock, TxStatus, + Error, Result, TxInBlock, }; -use anyhow::anyhow; -use gear_core::{ - ids::*, - memory::{PageBuf, PageBufInner}, -}; -use hex::ToHex; -use parity_scale_codec::Encode; +use gear_core::ids::*; use sp_runtime::AccountId32; use std::sync::Arc; -use subxt::{ - blocks::ExtrinsicEvents, - dynamic::Value, - metadata::EncodeWithMetadata, - storage::StorageAddress, - tx::{DynamicPayload, TxProgress}, - utils::Static, - Error as SubxtError, OnlineClient, -}; +use subxt::{blocks::ExtrinsicEvents, dynamic::Value}; -type TxProgressT = TxProgress>; type EventsResult = Result, Error>; /// Implementation of calls to programs/other users for [`Signer`]. #[derive(Clone)] -pub struct SignerCalls(pub(crate) Arc); - -/// Implementation of storage calls for [`Signer`]. -#[derive(Clone)] -pub struct SignerStorage(pub(crate) Arc); +pub struct SignerCalls(pub(crate) Arc); // pallet-balances impl SignerCalls { @@ -194,257 +164,34 @@ impl SignerCalls { } // pallet-utility -impl SignerInner { +impl SignerCalls { /// `pallet_utility::force_batch` pub async fn force_batch(&self, calls: Vec) -> Result { - self.run_tx( - UtilityCall::ForceBatch, - vec![calls.into_iter().map(Value::from).collect::>()], - ) - .await + self.0 + .run_tx( + UtilityCall::ForceBatch, + vec![calls.into_iter().map(Value::from).collect::>()], + ) + .await } } // pallet-sudo -impl SignerInner { - pub async fn process_sudo(&self, tx: DynamicPayload) -> EventsResult { - let tx = self.process(tx).await?; - let events = tx.wait_for_success().await?; - for event in events.iter() { - let event = event?.as_root_event::()?; - if let Event::Sudo(SudoEvent::Sudid { - sudo_result: Err(err), - }) = event - { - return Err(self.api().decode_error(err).into()); - } - } - - Ok(events) - } - +impl SignerCalls { /// `pallet_sudo::sudo_unchecked_weight` pub async fn sudo_unchecked_weight(&self, call: RuntimeCall, weight: Weight) -> EventsResult { - self.sudo_run_tx( - SudoCall::SudoUncheckedWeight, - // As `call` implements conversion to `Value`. - vec![ - call.into(), - Value::named_composite([ - ("ref_time", Value::u128(weight.ref_time as u128)), - ("proof_size", Value::u128(weight.proof_size as u128)), - ]), - ], - ) - .await - } - - /// `pallet_sudo::sudo` - pub async fn sudo(&self, call: RuntimeCall) -> EventsResult { - self.sudo_run_tx(SudoCall::Sudo, vec![Value::from(call)]) - .await - } -} - -// pallet-system -impl SignerStorage { - /// Sets storage values via calling sudo pallet - pub async fn set_storage(&self, items: &[(impl StorageAddress, impl Encode)]) -> EventsResult { - let metadata = self.0.api().metadata(); - let mut items_to_set = Vec::with_capacity(items.len()); - for item in items { - let item_key = storage_address_bytes(&item.0, &metadata)?; - let mut item_value_bytes = Vec::new(); - let item_value_type_id = crate::storage::storage_type_id(&metadata, &item.0)?; - Static(&item.1).encode_with_metadata( - item_value_type_id, - &metadata, - &mut item_value_bytes, - )?; - items_to_set.push((item_key, item_value_bytes)); - } - self.0 - .sudo(RuntimeCall::System(Call::set_storage { - items: items_to_set, - })) - .await - } -} - -// pallet-gas -impl SignerStorage { - /// Writes gas total issuance into storage. - pub async fn set_total_issuance(&self, value: u64) -> EventsResult { - let addr = Api::storage_root(GearGasStorage::TotalIssuance); - self.set_storage(&[(addr, value)]).await - } - - /// Writes Gear gas nodes into storage at their ids. - pub async fn set_gas_nodes( - &self, - gas_nodes: &impl AsRef<[(GearGasNodeId, GearGasNode)]>, - ) -> EventsResult { - let gas_nodes = gas_nodes.as_ref(); - let mut gas_nodes_to_set = Vec::with_capacity(gas_nodes.len()); - for gas_node in gas_nodes { - let addr = Api::storage(GearGasStorage::GasNodes, vec![Static(gas_node.0)]); - gas_nodes_to_set.push((addr, &gas_node.1)); - } - self.set_storage(&gas_nodes_to_set).await - } -} - -// pallet-gear-bank -impl SignerStorage { - /// Writes given BankAccount info into storage at `AccountId32`. - pub async fn set_bank_account_storage( - &self, - dest: impl Into, - value: BankAccount, - ) -> EventsResult { - let addr = Api::storage(GearBankStorage::Bank, vec![Value::from_bytes(dest.into())]); - self.set_storage(&[(addr, value)]).await - } -} - -// pallet-gear-program -impl SignerStorage { - /// Writes `InstrumentedCode` length into storage at `CodeId` - pub async fn set_code_len_storage(&self, code_id: CodeId, code_len: u32) -> EventsResult { - let addr = Api::storage( - GearProgramStorage::CodeLenStorage, - vec![Value::from_bytes(code_id)], - ); - self.set_storage(&[(addr, code_len)]).await - } - - /// Writes `InstrumentedCode` into storage at `CodeId` - pub async fn set_code_storage(&self, code_id: CodeId, code: &InstrumentedCode) -> EventsResult { - let addr = Api::storage( - GearProgramStorage::CodeStorage, - vec![Value::from_bytes(code_id)], - ); - self.set_storage(&[(addr, code)]).await - } - - /// Writes `GearPages` into storage at `program_id` - pub async fn set_gpages( - &self, - program_id: ProgramId, - program_pages: &GearPages, - ) -> EventsResult { - let mut program_pages_to_set = Vec::with_capacity(program_pages.len()); - for program_page in program_pages { - let addr = Api::storage( - GearProgramStorage::MemoryPageStorage, + .sudo_run_tx( + SudoCall::SudoUncheckedWeight, + // As `call` implements conversion to `Value`. vec![ - subxt::dynamic::Value::from_bytes(program_id), - subxt::dynamic::Value::u128(*program_page.0 as u128), + call.into(), + Value::named_composite([ + ("ref_time", Value::u128(weight.ref_time as u128)), + ("proof_size", Value::u128(weight.proof_size as u128)), + ]), ], - ); - let page_buf_inner = PageBufInner::try_from(program_page.1.clone()) - .map_err(|_| Error::PageInvalid(*program_page.0, program_id.encode_hex()))?; - let value = PageBuf::from_inner(page_buf_inner); - program_pages_to_set.push((addr, value)); - } - self.set_storage(&program_pages_to_set).await - } - - /// Writes `ActiveProgram` into storage at `program_id` - pub async fn set_gprog( - &self, - program_id: ProgramId, - program: ActiveProgram, - ) -> EventsResult { - let addr = Api::storage( - GearProgramStorage::ProgramStorage, - vec![Value::from_bytes(program_id)], - ); - self.set_storage(&[(addr, &Program::Active(program))]).await - } -} - -// Singer utils -impl SignerInner { - /// Propagates log::info for given status. - pub(crate) fn log_status(&self, status: &TxStatus) { - match status { - TxStatus::Future => log::info!(" Status: Future"), - TxStatus::Ready => log::info!(" Status: Ready"), - TxStatus::Broadcast(v) => log::info!(" Status: Broadcast( {v:?} )"), - TxStatus::InBlock(b) => log::info!( - " Status: InBlock( block hash: {}, extrinsic hash: {} )", - b.block_hash(), - b.extrinsic_hash() - ), - TxStatus::Retracted(h) => log::warn!(" Status: Retracted( {h} )"), - TxStatus::FinalityTimeout(h) => log::error!(" Status: FinalityTimeout( {h} )"), - TxStatus::Finalized(b) => log::info!( - " Status: Finalized( block hash: {}, extrinsic hash: {} )", - b.block_hash(), - b.extrinsic_hash() - ), - TxStatus::Usurped(h) => log::error!(" Status: Usurped( {h} )"), - TxStatus::Dropped => log::error!(" Status: Dropped"), - TxStatus::Invalid => log::error!(" Status: Invalid"), - } - } - - /// Wrapper for submit and watch with nonce. - async fn sign_and_submit_then_watch<'a>( - &self, - tx: &DynamicPayload, - ) -> Result { - if let Some(nonce) = self.nonce { - self.api - .tx() - .create_signed_with_nonce(tx, &self.signer, nonce, Default::default())? - .submit_and_watch() - .await - } else { - self.api - .tx() - .sign_and_submit_then_watch_default(tx, &self.signer) - .await - } - } - - /// Listen transaction process and print logs. - pub async fn process<'a>(&self, tx: DynamicPayload) -> Result { - use subxt::tx::TxStatus::*; - - let signer_rpc = SignerRpc(Arc::new(self.clone())); - let before = signer_rpc.get_balance().await?; - - let mut process = self.sign_and_submit_then_watch(&tx).await?; - let (pallet, name) = (tx.pallet_name(), tx.call_name()); - - log::info!("Submitted extrinsic {}::{}", pallet, name); - - while let Some(status) = process.next_item().await { - let status = status?; - self.log_status(&status); - match status { - Future | Ready | Broadcast(_) | InBlock(_) | Retracted(_) => (), - Finalized(b) => { - log::info!( - "Successfully submitted call {}::{} {} at {}!", - pallet, - name, - b.extrinsic_hash(), - b.block_hash() - ); - self.log_balance_spent(before).await?; - return Ok(b); - } - _ => { - self.log_balance_spent(before).await?; - return Err(status.into()); - } - } - } - - Err(anyhow!("Transaction wasn't found").into()) + ) + .await } } diff --git a/gsdk/src/signer/mod.rs b/gsdk/src/signer/mod.rs index d8e1f1161b5..e1136977ba8 100644 --- a/gsdk/src/signer/mod.rs +++ b/gsdk/src/signer/mod.rs @@ -19,31 +19,34 @@ //! Gear api with signer use crate::{ + backtrace::Backtrace, config::GearConfig, result::{Error, Result}, Api, }; -use calls::{SignerCalls, SignerStorage}; +use calls::SignerCalls; use core::ops::Deref; pub use pair_signer::PairSigner; use rpc::SignerRpc; use sp_core::{crypto::Ss58Codec, sr25519::Pair, Pair as PairT}; use sp_runtime::AccountId32; use std::sync::Arc; +use storage::SignerStorage; mod calls; mod pair_signer; mod rpc; +mod storage; mod utils; /// Signer representation that provides access to gear API. -/// Implements low-level methods such as [`run_tx`](`SignerInner::run_tx`) -/// and [`force_batch`](`SignerInner::force_batch`). +/// Implements low-level methods such as [`run_tx`](`Inner::run_tx`) +/// and [`force_batch`](`Signer.calls()::force_batch`). /// Other higher-level calls are provided by [`Signer::storage`], /// [`Signer::calls`], [`Signer::rpc`]. #[derive(Clone)] pub struct Signer { - signer: Arc, + signer: Arc, /// Calls that get or set storage. pub storage: SignerStorage, /// Calls for interaction with on-chain programs. @@ -54,28 +57,35 @@ pub struct Signer { /// Implementation of low-level calls for [`Signer`]. #[derive(Clone)] -pub struct SignerInner { +pub struct Inner { api: Api, /// Current signer. signer: PairSigner, nonce: Option, + backtrace: Backtrace, } impl Signer { + /// Get backtrace of the signer. + pub fn backtrace(&self) -> Backtrace { + self.calls.0.backtrace.clone() + } + /// New signer api. pub fn new(api: Api, suri: &str, passwd: Option<&str>) -> Result { - let signer = SignerInner { + let signer = Inner { api, signer: PairSigner::new( Pair::from_string(suri, passwd).map_err(|_| Error::InvalidSecret)?, ), nonce: None, + backtrace: Default::default(), }; Ok(Self::from_inner(signer)) } - fn from_inner(signer: SignerInner) -> Self { + fn from_inner(signer: Inner) -> Self { let signer = Arc::new(signer); Self { @@ -86,8 +96,10 @@ impl Signer { } } - #[deny(unused_variables)] - fn replace_inner(&mut self, inner: SignerInner) { + fn replace_inner(&mut self, mut inner: Inner) { + let backtrace = self.backtrace(); + inner.backtrace = backtrace; + let Signer { signer, storage, @@ -106,7 +118,7 @@ impl Signer { let signer = PairSigner::new(Pair::from_string(suri, passwd).map_err(|_| Error::InvalidSecret)?); - self.replace_inner(SignerInner { + self.replace_inner(Inner { signer, ..self.signer.as_ref().clone() }); @@ -116,14 +128,14 @@ impl Signer { /// Set nonce of the signer pub fn set_nonce(&mut self, nonce: u32) { - self.replace_inner(SignerInner { + self.replace_inner(Inner { nonce: Some(nonce), ..self.signer.as_ref().clone() }); } } -impl SignerInner { +impl Inner { /// Get address of the current signer pub fn address(&self) -> String { self.account_id().to_ss58check() @@ -142,10 +154,11 @@ impl SignerInner { impl From<(Api, PairSigner)> for Signer { fn from((api, signer): (Api, PairSigner)) -> Self { - let signer = SignerInner { + let signer = Inner { api, signer, nonce: None, + backtrace: Backtrace::default(), }; Self::from_inner(signer) @@ -153,9 +166,9 @@ impl From<(Api, PairSigner)> for Signer { } impl Deref for Signer { - type Target = SignerInner; + type Target = Inner; - fn deref(&self) -> &SignerInner { + fn deref(&self) -> &Inner { self.signer.as_ref() } } diff --git a/gsdk/src/signer/rpc.rs b/gsdk/src/signer/rpc.rs index 912531fd809..4a9e5413ba5 100644 --- a/gsdk/src/signer/rpc.rs +++ b/gsdk/src/signer/rpc.rs @@ -18,14 +18,14 @@ //! RPC calls with signer -use crate::{result::Result, signer::SignerInner, GasInfo}; +use crate::{result::Result, signer::Inner, GasInfo}; use gear_core::ids::{CodeId, MessageId, ProgramId}; use sp_core::H256; use std::sync::Arc; /// Implementation of calls to node RPC for [`Signer`]. #[derive(Clone)] -pub struct SignerRpc(pub(crate) Arc); +pub struct SignerRpc(pub(crate) Arc); impl SignerRpc { /// public key of the signer in H256 diff --git a/gsdk/src/signer/storage.rs b/gsdk/src/signer/storage.rs new file mode 100644 index 00000000000..25628995559 --- /dev/null +++ b/gsdk/src/signer/storage.rs @@ -0,0 +1,172 @@ +// This file is part of Gear. +// +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Storage interfaces +use crate::{ + config::GearConfig, + metadata::{ + gear_runtime::RuntimeCall, + runtime_types::{ + frame_system::pallet::Call, + gear_common::{ActiveProgram, Program}, + gear_core::code::InstrumentedCode, + pallet_gear_bank::pallet::BankAccount, + }, + storage::{GearBankStorage, GearGasStorage, GearProgramStorage}, + }, + signer::Inner, + utils::storage_address_bytes, + Api, BlockNumber, Error, GearGasNode, GearGasNodeId, GearPages, Result, +}; +use gear_core::{ + ids::*, + memory::{PageBuf, PageBufInner}, +}; +use hex::ToHex; +use parity_scale_codec::Encode; +use sp_runtime::AccountId32; +use std::sync::Arc; +use subxt::{ + blocks::ExtrinsicEvents, dynamic::Value, metadata::EncodeWithMetadata, storage::StorageAddress, + utils::Static, +}; + +type EventsResult = Result, Error>; + +/// Implementation of storage calls for [`Signer`]. +#[derive(Clone)] +pub struct SignerStorage(pub(crate) Arc); + +// pallet-system +impl SignerStorage { + /// Sets storage values via calling sudo pallet + pub async fn set_storage(&self, items: &[(impl StorageAddress, impl Encode)]) -> EventsResult { + let metadata = self.0.api().metadata(); + let mut items_to_set = Vec::with_capacity(items.len()); + for item in items { + let item_key = storage_address_bytes(&item.0, &metadata)?; + let mut item_value_bytes = Vec::new(); + let item_value_type_id = crate::storage::storage_type_id(&metadata, &item.0)?; + Static(&item.1).encode_with_metadata( + item_value_type_id, + &metadata, + &mut item_value_bytes, + )?; + items_to_set.push((item_key, item_value_bytes)); + } + + self.0 + .sudo(RuntimeCall::System(Call::set_storage { + items: items_to_set, + })) + .await + } +} + +// pallet-gas +impl SignerStorage { + /// Writes gas total issuance into storage. + pub async fn set_total_issuance(&self, value: u64) -> EventsResult { + let addr = Api::storage_root(GearGasStorage::TotalIssuance); + self.set_storage(&[(addr, value)]).await + } + + /// Writes Gear gas nodes into storage at their ids. + pub async fn set_gas_nodes( + &self, + gas_nodes: &impl AsRef<[(GearGasNodeId, GearGasNode)]>, + ) -> EventsResult { + let gas_nodes = gas_nodes.as_ref(); + let mut gas_nodes_to_set = Vec::with_capacity(gas_nodes.len()); + for gas_node in gas_nodes { + let addr = Api::storage(GearGasStorage::GasNodes, vec![Static(gas_node.0)]); + gas_nodes_to_set.push((addr, &gas_node.1)); + } + self.set_storage(&gas_nodes_to_set).await + } +} + +// pallet-gear-bank +impl SignerStorage { + /// Writes given BankAccount info into storage at `AccountId32`. + pub async fn set_bank_account_storage( + &self, + dest: impl Into, + value: BankAccount, + ) -> EventsResult { + let addr = Api::storage(GearBankStorage::Bank, vec![Value::from_bytes(dest.into())]); + self.set_storage(&[(addr, value)]).await + } +} + +// pallet-gear-program +impl SignerStorage { + /// Writes `InstrumentedCode` length into storage at `CodeId` + pub async fn set_code_len_storage(&self, code_id: CodeId, code_len: u32) -> EventsResult { + let addr = Api::storage( + GearProgramStorage::CodeLenStorage, + vec![Value::from_bytes(code_id)], + ); + self.set_storage(&[(addr, code_len)]).await + } + + /// Writes `InstrumentedCode` into storage at `CodeId` + pub async fn set_code_storage(&self, code_id: CodeId, code: &InstrumentedCode) -> EventsResult { + let addr = Api::storage( + GearProgramStorage::CodeStorage, + vec![Value::from_bytes(code_id)], + ); + self.set_storage(&[(addr, code)]).await + } + + /// Writes `GearPages` into storage at `program_id` + pub async fn set_gpages( + &self, + program_id: ProgramId, + program_pages: &GearPages, + ) -> EventsResult { + let mut program_pages_to_set = Vec::with_capacity(program_pages.len()); + for program_page in program_pages { + let addr = Api::storage( + GearProgramStorage::MemoryPageStorage, + vec![ + subxt::dynamic::Value::from_bytes(program_id), + subxt::dynamic::Value::u128(*program_page.0 as u128), + ], + ); + let page_buf_inner = PageBufInner::try_from(program_page.1.clone()) + .map_err(|_| Error::PageInvalid(*program_page.0, program_id.encode_hex()))?; + let value = PageBuf::from_inner(page_buf_inner); + program_pages_to_set.push((addr, value)); + } + self.set_storage(&program_pages_to_set).await + } + + /// Writes `ActiveProgram` into storage at `program_id` + pub async fn set_gprog( + &self, + program_id: ProgramId, + program: ActiveProgram, + ) -> EventsResult { + let addr = Api::storage( + GearProgramStorage::ProgramStorage, + vec![Value::from_bytes(program_id)], + ); + self.set_storage(&[(addr, &Program::Active(program))]).await + } +} diff --git a/gsdk/src/signer/utils.rs b/gsdk/src/signer/utils.rs index f4d88a5f4a5..24b56bef50b 100644 --- a/gsdk/src/signer/utils.rs +++ b/gsdk/src/signer/utils.rs @@ -18,18 +18,32 @@ //! Utils -use std::sync::Arc; - -use super::SignerInner; +use super::Inner; use crate::{ - config::GearConfig, metadata::CallInfo, result::Result, signer::SignerRpc, Error, TxInBlock, + backtrace::BacktraceStatus, + config::GearConfig, + metadata::{ + calls::SudoCall, gear_runtime::RuntimeCall, sudo::Event as SudoEvent, CallInfo, Event, + }, + result::Result, + signer::SignerRpc, + Error, TxInBlock, TxStatus, }; +use anyhow::anyhow; use scale_value::Composite; -use subxt::blocks::ExtrinsicEvents; +use sp_core::H256; +use std::sync::Arc; +use subxt::{ + blocks::ExtrinsicEvents, + dynamic::Value, + tx::{DynamicPayload, TxProgress}, + Error as SubxtError, OnlineClient, +}; +type TxProgressT = TxProgress>; type EventsResult = Result, Error>; -impl SignerInner { +impl Inner { /// Logging balance spent pub async fn log_balance_spent(&self, before: u128) -> Result<()> { let signer_rpc = SignerRpc(Arc::new(self.clone())); @@ -39,6 +53,107 @@ impl SignerInner { Ok(()) } + /// Propagates log::info for given status. + pub(crate) fn log_status(status: &TxStatus) { + match status { + TxStatus::Future => log::info!(" Status: Future"), + TxStatus::Ready => log::info!(" Status: Ready"), + TxStatus::Broadcast(v) => log::info!(" Status: Broadcast( {v:?} )"), + TxStatus::InBlock(b) => log::info!( + " Status: InBlock( block hash: {}, extrinsic hash: {} )", + b.block_hash(), + b.extrinsic_hash() + ), + TxStatus::Retracted(h) => log::warn!(" Status: Retracted( {h} )"), + TxStatus::FinalityTimeout(h) => log::error!(" Status: FinalityTimeout( {h} )"), + TxStatus::Finalized(b) => log::info!( + " Status: Finalized( block hash: {}, extrinsic hash: {} )", + b.block_hash(), + b.extrinsic_hash() + ), + TxStatus::Usurped(h) => log::error!(" Status: Usurped( {h} )"), + TxStatus::Dropped => log::error!(" Status: Dropped"), + TxStatus::Invalid => log::error!(" Status: Invalid"), + } + } + + /// Listen transaction process and print logs. + pub async fn process<'a>(&self, tx: DynamicPayload) -> Result { + use subxt::tx::TxStatus::*; + + let signer_rpc = SignerRpc(Arc::new(self.clone())); + let before = signer_rpc.get_balance().await?; + + let mut process = self.sign_and_submit_then_watch(&tx).await?; + let (pallet, name) = (tx.pallet_name(), tx.call_name()); + + log::info!("Submitted extrinsic {}::{}", pallet, name); + + let mut queue: Vec = Default::default(); + let mut hash: Option = None; + + while let Some(status) = process.next_item().await { + let status = status?; + Self::log_status(&status); + + if let Some(h) = &hash { + self.backtrace + .clone() + .append(*h, BacktraceStatus::from(&status)); + } else { + queue.push((&status).into()); + } + + match status { + Future | Ready | Broadcast(_) | Retracted(_) => (), + InBlock(b) => { + hash = Some(b.extrinsic_hash()); + self.backtrace.append( + b.extrinsic_hash(), + BacktraceStatus::InBlock { + block_hash: b.block_hash(), + extrinsic_hash: b.extrinsic_hash(), + }, + ); + } + Finalized(b) => { + log::info!( + "Successfully submitted call {}::{} {} at {}!", + pallet, + name, + b.extrinsic_hash(), + b.block_hash() + ); + self.log_balance_spent(before).await?; + return Ok(b); + } + _ => { + self.log_balance_spent(before).await?; + return Err(status.into()); + } + } + } + + Err(anyhow!("Transaction wasn't found").into()) + } + + /// Process sudo transaction. + pub async fn process_sudo(&self, tx: DynamicPayload) -> EventsResult { + let tx = self.process(tx).await?; + let events = tx.wait_for_success().await?; + for event in events.iter() { + let event = event?.as_root_event::()?; + if let Event::Sudo(SudoEvent::Sudid { + sudo_result: Err(err), + }) = event + { + return Err(self.api().decode_error(err).into()); + } + } + + Ok(events) + } + /// Run transaction. /// /// This function allows us to execute any transactions in gear. @@ -98,4 +213,29 @@ impl SignerInner { self.process_sudo(tx).await } + + /// `pallet_sudo::sudo` + pub async fn sudo(&self, call: RuntimeCall) -> EventsResult { + self.sudo_run_tx(SudoCall::Sudo, vec![Value::from(call)]) + .await + } + + /// Wrapper for submit and watch with nonce. + async fn sign_and_submit_then_watch<'a>( + &self, + tx: &DynamicPayload, + ) -> Result { + if let Some(nonce) = self.nonce { + self.api + .tx() + .create_signed_with_nonce(tx, &self.signer, nonce, Default::default())? + .submit_and_watch() + .await + } else { + self.api + .tx() + .sign_and_submit_then_watch_default(tx, &self.signer) + .await + } + } } diff --git a/gsdk/tests/backtrace.rs b/gsdk/tests/backtrace.rs new file mode 100644 index 00000000000..2907371c0ac --- /dev/null +++ b/gsdk/tests/backtrace.rs @@ -0,0 +1,45 @@ +// This file is part of Gear. +// +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gsdk::{backtrace::BacktraceStatus, signer::Signer, Api, Result}; +use utils::{alice_account_id, dev_node, node_uri}; + +mod utils; + +#[tokio::test] +async fn transfer_backtrace() -> Result<()> { + let node = dev_node(); + let api = Api::new(Some(&node_uri(&node))).await?; + let signer = Signer::new(api, "//Alice", None)?; + let alice: [u8; 32] = *alice_account_id().as_ref(); + + let tx = signer.calls.transfer(alice, 42).await?; + let backtrace = signer + .backtrace() + .get(tx.extrinsic_hash()) + .expect("Failed to get backtrace of transfer"); + + assert!(matches!( + backtrace.values().collect::>()[..], + [ + BacktraceStatus::InBlock { .. }, + BacktraceStatus::Finalized { .. }, + ] + )); + Ok(()) +} diff --git a/gsdk/tests/rpc.rs b/gsdk/tests/rpc.rs index 376af7741de..8aef161cd86 100644 --- a/gsdk/tests/rpc.rs +++ b/gsdk/tests/rpc.rs @@ -19,42 +19,14 @@ //! Requires node to be built in release mode use gear_core::ids::{CodeId, ProgramId}; -use gsdk::{ - ext::{sp_core::crypto::Ss58Codec, sp_runtime::AccountId32}, - testing::Node, - Api, Error, Result, -}; +use gsdk::{Api, Error, Result}; use jsonrpsee::types::error::{CallError, ErrorObject}; use parity_scale_codec::Encode; use std::{borrow::Cow, process::Command, str::FromStr}; use subxt::{config::Header, error::RpcError, Error as SubxtError}; +use utils::{alice_account_id, dev_node, node_uri}; -fn dev_node() -> Node { - // Use release build because of performance reasons. - let bin_path = env!("CARGO_MANIFEST_DIR").to_owned() + "/../target/release/gear"; - - #[cfg(not(feature = "vara-testing"))] - let args = vec!["--tmp", "--dev"]; - #[cfg(feature = "vara-testing")] - let args = vec![ - "--tmp", - "--chain=vara-dev", - "--alice", - "--validator", - "--reserved-only", - ]; - - Node::try_from_path(bin_path, args) - .expect("Failed to start node: Maybe it isn't built with --release flag?") -} - -fn node_uri(node: &Node) -> String { - format!("ws://{}", &node.address()) -} - -fn alice_account_id() -> AccountId32 { - AccountId32::from_ss58check("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap() -} +mod utils; #[tokio::test] async fn test_calculate_upload_gas() -> Result<()> { diff --git a/gsdk/tests/utils/mod.rs b/gsdk/tests/utils/mod.rs new file mode 100644 index 00000000000..6c0696c2b28 --- /dev/null +++ b/gsdk/tests/utils/mod.rs @@ -0,0 +1,49 @@ +// This file is part of Gear. +// +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use gsdk::{ + ext::{sp_core::crypto::Ss58Codec, sp_runtime::AccountId32}, + testing::Node, +}; + +pub fn dev_node() -> Node { + // Use release build because of performance reasons. + let bin_path = env!("CARGO_MANIFEST_DIR").to_owned() + "/../target/release/gear"; + + #[cfg(not(feature = "vara-testing"))] + let args = vec!["--tmp", "--dev"]; + #[cfg(feature = "vara-testing")] + let args = vec![ + "--tmp", + "--chain=vara-dev", + "--alice", + "--validator", + "--reserved-only", + ]; + + Node::try_from_path(bin_path, args) + .expect("Failed to start node: Maybe it isn't built with --release flag?") +} + +pub fn node_uri(node: &Node) -> String { + format!("ws://{}", &node.address()) +} + +pub fn alice_account_id() -> AccountId32 { + AccountId32::from_ss58check("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap() +} diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 54a41e9bd16..48cbe8ebfbb 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -456,7 +456,11 @@ impl ExtManager { /// Call non-void meta function from actor stored in manager. /// Warning! This is a static call that doesn't change actors pages data. - pub(crate) fn read_state_bytes(&mut self, program_id: &ProgramId) -> Result> { + pub(crate) fn read_state_bytes( + &mut self, + payload: Vec, + program_id: &ProgramId, + ) -> Result> { let (actor, _balance) = self .actors .get_mut(program_id) @@ -468,7 +472,7 @@ impl ExtManager { program.code().clone(), Some(program.allocations().clone()), Some(*program_id), - Default::default(), + payload, u64::MAX, self.block_info, ) @@ -484,6 +488,7 @@ impl ExtManager { pub(crate) fn read_state_bytes_using_wasm( &mut self, + payload: Vec, program_id: &ProgramId, fn_name: &str, wasm: Vec, @@ -501,7 +506,7 @@ impl ExtManager { .0; let mut mapping_code_payload = args.unwrap_or_default(); - mapping_code_payload.append(&mut self.read_state_bytes(program_id)?); + mapping_code_payload.append(&mut self.read_state_bytes(payload, program_id)?); core_processor::informational::execute_for_reply::, _>( String::from(fn_name), diff --git a/gtest/src/program.rs b/gtest/src/program.rs index dca23cfb66a..674e92f1531 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -446,10 +446,10 @@ impl<'a> Program<'a> { } /// Reads the program’s state as a byte vector. - pub fn read_state_bytes(&self) -> Result> { + pub fn read_state_bytes(&self, payload: Vec) -> Result> { self.manager .borrow_mut() - .with_externalities(|this| this.read_state_bytes(&self.id)) + .with_externalities(|this| this.read_state_bytes(payload, &self.id)) } /// Reads the program’s transformed state as a byte vector. The transformed @@ -482,36 +482,37 @@ impl<'a> Program<'a> { /// # let ARG_2 = 0u8; /// //Read state bytes with no arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Option::>::None)?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Option::>::None)?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!())?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!())?; /// // Read state bytes with one argument passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some(ARG_1.encode()))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Some(ARG_1.encode()))?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!(ARG_1))?; /// // Read state bytes with multiple arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2).encode()))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Some((ARG_1, ARG_2).encode()))?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1, ARG_2))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!(ARG_1, ARG_2))?; /// # Ok(()) /// # } /// ``` pub fn read_state_bytes_using_wasm( &self, + payload: Vec, fn_name: &str, wasm: Vec, args: Option>, ) -> Result> { self.manager.borrow_mut().with_externalities(|this| { - this.read_state_bytes_using_wasm(&self.id, fn_name, wasm, args) + this.read_state_bytes_using_wasm(payload, &self.id, fn_name, wasm, args) }) } /// Reads and decodes the program's state . - pub fn read_state(&self) -> Result { - let state_bytes = self.read_state_bytes()?; + pub fn read_state(&self, payload: P) -> Result { + let state_bytes = self.read_state_bytes(payload.encode())?; D::decode(&mut state_bytes.as_ref()).map_err(Into::into) } @@ -544,30 +545,32 @@ impl<'a> Program<'a> { /// # let ARG_2 = 0u8; /// //Read state bytes with no arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Option::<()>::None)?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Option::<()>::None)?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!())?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!())?; /// // Read state bytes with one argument passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Some(ARG_1))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Some(ARG_1))?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!(ARG_1))?; /// // Read state bytes with multiple arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2)))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Some((ARG_1, ARG_2)))?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1, ARG_2))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!(ARG_1, ARG_2))?; /// # Ok(()) /// # } /// ``` - pub fn read_state_using_wasm( + pub fn read_state_using_wasm( &self, + payload: P, fn_name: &str, wasm: Vec, argument: Option, ) -> Result { let argument_bytes = argument.map(|arg| arg.encode()); - let state_bytes = self.read_state_bytes_using_wasm(fn_name, wasm, argument_bytes)?; + let state_bytes = + self.read_state_bytes_using_wasm(payload.encode(), fn_name, wasm, argument_bytes)?; D::decode(&mut state_bytes.as_ref()).map_err(Into::into) } diff --git a/node/authorship/Cargo.toml b/node/authorship/Cargo.toml index 7b474e043a6..2e59fc84df7 100644 --- a/node/authorship/Cargo.toml +++ b/node/authorship/Cargo.toml @@ -42,6 +42,7 @@ frame-system = { workspace = true, features = ["std"] } prometheus-endpoint.workspace = true [dev-dependencies] +common = { workspace = true, features = ["std"] } sc-transaction-pool.workspace = true frame-support = { workspace = true, features = ["std"] } sp-io = { workspace = true, features = ["std"] } @@ -54,8 +55,9 @@ pallet-timestamp = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-gear = { workspace = true, features = ["std"] } pallet-gear-messenger = { workspace = true, features = ["std"] } +pallet-gear-program = { workspace = true, features = ["std"] } testing = {workspace = true, features = ["vara-native"] } vara-runtime = { workspace = true, features = ["std", "dev"] } -demo-mul-by-const.workspace = true +demo-mul-by-const = { workspace = true, features = ["debug"] } env_logger.workspace = true service = { workspace = true, features = ["dev", "vara-native"] } diff --git a/node/authorship/src/tests.rs b/node/authorship/src/tests.rs index afbc17b4bbd..c845fbbf692 100644 --- a/node/authorship/src/tests.rs +++ b/node/authorship/src/tests.rs @@ -23,10 +23,12 @@ use crate::authorship::*; -use codec::Encode; +use codec::{Decode, Encode}; +use common::Program; use core::convert::TryFrom; use frame_support::{storage::storage_prefix, traits::PalletInfoAccess}; use futures::executor::block_on; +use runtime_primitives::BlockNumber; use sc_client_api::Backend; use sc_transaction_pool::BasicPool; use sc_transaction_pool_api::{ @@ -53,6 +55,7 @@ use testing::{ use vara_runtime::{AccountId, Runtime, RuntimeCall, UncheckedExtrinsic, SLOT_DURATION, VERSION}; const SOURCE: TransactionSource = TransactionSource::External; +const DEFAULT_GAS_LIMIT: u64 = 865_000_000; fn chain_event(header: B::Header) -> ChainEvent where @@ -91,7 +94,7 @@ fn checked_extrinsics(n: u32, signer: AccountId, nonce: &mut u32) -> Vec::name().as_bytes(), + "ProgramStorage".as_bytes(), + ); + let mut iter_args = IterArgs::default(); + iter_args.prefix = Some(&programs_prefix); + + // The fact that 2 init messages out of 5 have been processed means + // that there should be 2 inited programs. + let inited_count = state.pairs(iter_args).unwrap().fold(0u32, |count, pair| { + let value = match pair { + Ok((_key, value)) => value, + _ => return count, + }; + + match Program::::decode(&mut &value[..]) { + Ok(p) if p.is_initialized() => count + 1, + _ => count, + } + }); + assert_eq!(inited_count, 2); } #[test] diff --git a/pallets/gear/rpc/Cargo.toml b/pallets/gear/rpc/Cargo.toml index 366c3bb3a48..505792028f9 100644 --- a/pallets/gear/rpc/Cargo.toml +++ b/pallets/gear/rpc/Cargo.toml @@ -18,7 +18,7 @@ sp-rpc.workspace = true sp-runtime.workspace = true # Local packages -gear-core.workspace = true +gear-core = { workspace = true, features = ["std"] } gear-core-errors = { workspace = true, features = ["codec"] } gear-common.workspace = true pallet-gear-rpc-runtime-api.workspace = true diff --git a/pallets/gear/src/lib.rs b/pallets/gear/src/lib.rs index b1a83d27c97..f6a6c22423a 100644 --- a/pallets/gear/src/lib.rs +++ b/pallets/gear/src/lib.rs @@ -43,6 +43,7 @@ pub use crate::{ pallet::*, schedule::{HostFnWeights, InstructionWeights, Limits, MemoryWeights, Schedule}, }; +pub use gear_core::gas::GasInfo; pub use weights::WeightInfo; use alloc::{format, string::String}; @@ -139,27 +140,6 @@ impl DebugInfo for () { } } -/// The struct contains results of gas calculation required to process -/// a message. -#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub struct GasInfo { - /// Represents minimum gas limit required for execution. - pub min_limit: u64, - /// Gas amount that we reserve for some other on-chain interactions. - pub reserved: u64, - /// Contains number of gas burned during message processing. - pub burned: u64, - /// The value may be returned if a program happens to be executed - /// the second or next time in a block. - pub may_be_returned: u64, - /// Was the message placed into waitlist at the end of calculating. - /// - /// This flag shows, that `min_limit` makes sense and have some guarantees - /// only before insertion into waitlist. - pub waited: bool, -} - #[frame_support::pallet] pub mod pallet { use super::*; diff --git a/runtime/gear/src/lib.rs b/runtime/gear/src/lib.rs index da93e6fce77..9bedffb36e0 100644 --- a/runtime/gear/src/lib.rs +++ b/runtime/gear/src/lib.rs @@ -124,7 +124,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("gear"), apis: RUNTIME_API_VERSIONS, authoring_version: 1, - spec_version: 1000, + spec_version: 1010, impl_version: 1, transaction_version: 1, state_version: 1, diff --git a/runtime/vara/src/lib.rs b/runtime/vara/src/lib.rs index f0446824539..11b503790d2 100644 --- a/runtime/vara/src/lib.rs +++ b/runtime/vara/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // The version of the runtime specification. A full node will not attempt to use its native // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, // `spec_version`, and `authoring_version` are the same between Wasm and native. - spec_version: 1000, + spec_version: 1010, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/scripts/weight-diff.sh b/scripts/weight-diff.sh index ae52c2c55c3..11a1baed4db 100755 --- a/scripts/weight-diff.sh +++ b/scripts/weight-diff.sh @@ -39,18 +39,23 @@ flag=$4 dump_path="weight-dumps" mkdir -p "$dump_path" -set -x +if [ -z $CI ] ; then + set -x + CARGO_FLAGS="" +else + CARGO_FLAGS="--quiet" +fi git checkout "$branch1" dump_path1="$dump_path/${branch1//\//-}.json" -cargo run --package gear-weight-diff --release -- dump "$dump_path1" --label "$branch1" +cargo run $CARGO_FLAGS --package gear-weight-diff --release -- dump "$dump_path1" --label "$branch1" git checkout "$branch2" dump_path2="$dump_path/${branch2//\//-}.json" -cargo run --package gear-weight-diff --release -- dump "$dump_path2" --label "$branch2" +cargo run $CARGO_FLAGS --package gear-weight-diff --release -- dump "$dump_path2" --label "$branch2" git checkout "$current_branch" -cargo run --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "instruction" $flag -cargo run --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "host-fn" $flag -cargo run --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "memory" $flag +cargo run $CARGO_FLAGS --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "instruction" $flag +cargo run $CARGO_FLAGS --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "host-fn" $flag +cargo run $CARGO_FLAGS --package gear-weight-diff --release -- diff "$dump_path1" "$dump_path2" "$runtime" "memory" $flag diff --git a/utils/node-loader/src/utils.rs b/utils/node-loader/src/utils.rs index d63d12ea75a..e7640fa21c1 100644 --- a/utils/node-loader/src/utils.rs +++ b/utils/node-loader/src/utils.rs @@ -239,6 +239,7 @@ pub fn get_wasm_gen_config( injection_amounts, params_config, initial_pages: initial_pages as u32, + unreachable_enabled: false, ..Default::default() } } diff --git a/utils/runtime-fuzzer/src/arbitrary_call.rs b/utils/runtime-fuzzer/src/arbitrary_call.rs index 5f4d9f5836f..9882efb8f47 100644 --- a/utils/runtime-fuzzer/src/arbitrary_call.rs +++ b/utils/runtime-fuzzer/src/arbitrary_call.rs @@ -233,6 +233,7 @@ fn config( log_info, params_config, initial_pages: initial_pages as u32, + unreachable_enabled: false, ..Default::default() } } diff --git a/utils/wasm-gen/src/config.rs b/utils/wasm-gen/src/config.rs index 5993ab0283e..05ff15c2a1e 100644 --- a/utils/wasm-gen/src/config.rs +++ b/utils/wasm-gen/src/config.rs @@ -39,7 +39,8 @@ //! ], //! max_instructions: 100_000, //! min_funcs: 15, -//! max_funcs: 30 +//! max_funcs: 30, +//! unreachable_enabled: true, //! }; //! let arbitrary = ArbitraryParams::arbitrary(u)?; //! Ok((selectable_params, arbitrary).into()) @@ -140,7 +141,7 @@ pub struct StandardGearWasmConfigsBundle { pub existing_addresses: Option>, /// Flag which signals whether recursions must be removed. pub remove_recursion: bool, - /// Flag which signals whether `call_indirect` instruction must not be used + /// Flag which signals whether `call_indirect` instruction must be used /// during wasm generation. pub call_indirect_enabled: bool, /// Injection amount ranges for each sys-call. @@ -153,6 +154,9 @@ pub struct StandardGearWasmConfigsBundle { pub stack_end_page: Option, /// Sys-calls params config pub params_config: SysCallsParamsConfig, + /// Flag which signals whether `unreachable` instruction must be used + /// during wasm generation. + pub unreachable_enabled: bool, } impl Default for StandardGearWasmConfigsBundle { @@ -167,6 +171,7 @@ impl Default for StandardGearWasmConfigsBundle { initial_pages: DEFAULT_INITIAL_SIZE, stack_end_page: None, params_config: SysCallsParamsConfig::default(), + unreachable_enabled: true, } } } @@ -183,10 +188,12 @@ impl> ConfigsBundle for StandardGearWasmConfigsBundle { initial_pages, stack_end_page, params_config, + unreachable_enabled, } = self; let selectable_params = SelectableParams { call_indirect_enabled, + unreachable_enabled, ..SelectableParams::default() }; diff --git a/utils/wasm-gen/src/config/module.rs b/utils/wasm-gen/src/config/module.rs index 7b0323deed4..46ac8f927f7 100644 --- a/utils/wasm-gen/src/config/module.rs +++ b/utils/wasm-gen/src/config/module.rs @@ -85,6 +85,7 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig { max_instructions, min_funcs, max_funcs, + unreachable_enabled, } = selectable_params; let ArbitraryParams { @@ -173,6 +174,7 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig { table_max_size_required, memory_grow_enabled, call_indirect_enabled, + unreachable_instruction_enabled: unreachable_enabled, }) } } @@ -354,6 +356,9 @@ pub struct SelectableParams { /// Maximum amount of functions `wasm-gen` will insert /// into generated wasm. pub max_funcs: usize, + /// Flag signalizing whether `unreachable` instruction + /// must be used or not. + pub unreachable_enabled: bool, } impl Default for SelectableParams { @@ -366,6 +371,7 @@ impl Default for SelectableParams { max_instructions: 100_000, min_funcs: 15, max_funcs: 30, + unreachable_enabled: true, } } } diff --git a/utils/wasm-gen/src/tests.rs b/utils/wasm-gen/src/tests.rs index ab09d5081e8..c8489a0e98d 100644 --- a/utils/wasm-gen/src/tests.rs +++ b/utils/wasm-gen/src/tests.rs @@ -286,6 +286,7 @@ fn execute_wasm_with_syscall_injected( max_instructions: 0, min_funcs: 1, max_funcs: 1, + unreachable_enabled: true, }, ); diff --git a/utils/weight-diff/src/main.rs b/utils/weight-diff/src/main.rs index f99233311d9..a40f85b1090 100644 --- a/utils/weight-diff/src/main.rs +++ b/utils/weight-diff/src/main.rs @@ -66,13 +66,13 @@ enum Commands { }, } -#[derive(Debug, Clone, ValueEnum)] +#[derive(Debug, Copy, Clone, ValueEnum)] enum Runtime { Gear, Vara, } -#[derive(Debug, Clone, ValueEnum)] +#[derive(Debug, Copy, Clone, ValueEnum)] enum WeightsKind { Instruction, HostFn, @@ -247,7 +247,7 @@ fn main() { } } - println!("Comparison table for {runtime:?} runtime"); + println!("Comparison table for {runtime:?} runtime for {kind:?}"); println!(); let mut builder = Builder::default();